rails-react-ssr 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3912c1b48d7d967fd867a88e16704ca98cd407d89d9ec0ce81c5d84c2fd975c5
4
+ data.tar.gz: 3874620976e632c57a7f05cdfdb72d9953f11218cc90a81b79aac51187e6b8db
5
+ SHA512:
6
+ metadata.gz: 390e781fb0622497b051d6d1cd52bf7f306c6de19a24bae542692b64680d760e5f7f4ba3a8371bec1fd3c287ed8f41e1f09f11afa08a502fe4928965cf1c10ad
7
+ data.tar.gz: 8eda42ff7e39306e8a5af2130d8db038cccb6725f346c59529586f59be1c2f0728d31a0992f1f31fa12a634e94a34dcf58c452d4da7ca73a98a04b5ee127967b
data/CHANGELOG.md ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # RailsReactSSR
2
+
3
+ RailsReactSSR is a light weight JS server side rendering utility that takes advantage of `Webpacker` and `NodeJS`.
4
+
5
+ ## Motivation
6
+
7
+ In my latest project I designed my application to use Rails for my API endpoints and `ReactJS` with `react-router` to
8
+ handle routing and handle the front end. I needed a basic tool that would not add a lot of bloat and be able to handle
9
+ server side rendering while allowing me to process the response (i.e. handle redirects from the router).
10
+
11
+ ## Dependencies
12
+
13
+ - [Ruby On Rails](https://rubyonrails.org/)
14
+ - [Webpacker](https://github.com/rails/webpacker)
15
+ - [NodeJS](https://nodejs.org/)
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'rails-react-ssr'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle install
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install rails-react-ssr
32
+
33
+ ## Usage
34
+
35
+ `RailsReactSSR::ServerRunner.exec!(bundle, props:, outputTemp:, max_tries:, delay:)`
36
+
37
+ * `bundle` is the path or name of the bundle in the `app/javascript/packs` directory
38
+
39
+ (optional)
40
+
41
+ * `props` is a hash that will converted to a JSON plain object and passed to the server
42
+ * `outputTemp` is either:
43
+ * a boolean, where true will output the compiled server code to `tmp/ssr/[bundle].js`
44
+ * a string that is the full path to the file to write to
45
+ * `max_tries` is the number of retries when fetching the bundle from teh `webpack-dev-server`
46
+ * `delay` is the time in ms between each retry
47
+
48
+ #### Basic usage
49
+
50
+ ##### `server.js`
51
+ ```typescript jsx
52
+ // Some processing here
53
+
54
+ stdout(yourHtmlOutput);
55
+ ```
56
+
57
+ ##### Your controller
58
+ ```ruby
59
+ def index
60
+ render html: RailsReactSSR::ServerRunner.exec!('server.js')
61
+ end
62
+ ```
63
+
64
+ #### Passing properties to the server
65
+
66
+ ##### From the controller:
67
+
68
+ ```ruby
69
+ def index
70
+ render html: RailsReactSSR::ServerRunner.exec!('server.js', props: {current_user: current_user})
71
+ end
72
+ ```
73
+
74
+ ##### From the server code:
75
+
76
+ ```javascript
77
+ ...
78
+
79
+ // Do something with the user
80
+ console.log('Current user', serverProps.currentUser.username);
81
+
82
+ ...
83
+ ```
84
+
85
+ The keys in the properties passed to the server will be transformed to camelized strings.
86
+
87
+ #### Handling redirects with `React-Router-Web`
88
+
89
+ Below is an example of handling redirects with [`react-router`](https://reacttraining.com/react-router/).
90
+ The principle should be the same for any routing packages.
91
+
92
+ ##### `server.js`
93
+ ```typescript jsx
94
+ // Not the complete story
95
+
96
+ const context = {};
97
+
98
+ const RedirectWithStatus = ({from, to, status}) => {
99
+ return (
100
+ <Route
101
+ render={({ staticContext }) => {
102
+ // there is no `staticContext` on the client, so
103
+ // we need to guard against that here
104
+ if (staticContext) staticContext.status = status;
105
+ return <Redirect from={from} to={to} />;
106
+ }}
107
+ />
108
+ );
109
+ }
110
+
111
+ const markup = ReactDOMServer.renderToString(
112
+ <StaticRouter location={serverProps.location} context={context}>
113
+ <Switch>
114
+ <RedirectWithStatus
115
+ status={301}
116
+ from="/users"
117
+ to="/profiles" />
118
+ <RedirectWithStatus
119
+ status={302}
120
+ from="/courses"
121
+ to="/dashboard"
122
+ />
123
+ </Switch>
124
+ </StaticRouter>
125
+ );
126
+
127
+ const output = {
128
+ html: markup,
129
+ logs: recordedLogs,
130
+ redirect: context.url,
131
+ status: context.status
132
+ };
133
+
134
+ stdout(JSON.stringify(output));
135
+ ```
136
+ More details on SSR and `react-router` at https://reacttraining.com/react-router/web/guides/server-rendering
137
+
138
+ ##### Your controller
139
+ ```ruby
140
+ def index
141
+ output = RailsReactSSR::ServerRunner.exec!('server.js', props: {current_user: current_user, location: request.fullpath})
142
+
143
+ react_response = ActiveSupport::JSON.decode output.split(/[\r\n]+/).reject(&:empty?).last
144
+
145
+ react_response.deep_symbolize_keys!
146
+
147
+ if react_response[:redirect]
148
+ redirect_to react_response[:redirect], status: 302
149
+ else
150
+ render html: react_response[:html]
151
+ end
152
+ end
153
+ ```
154
+
155
+ ### Caching Example
156
+
157
+ To improve the response time from the server, you should consider caching.
158
+
159
+ Things to consider:
160
+ 1) Using a cache key that is not the same for every route if you are using a JS routing package.
161
+ 2) How large the response is form the JS server.
162
+
163
+ ```ruby
164
+
165
+ def index
166
+ ## Do something to the path to generate a key that represents it in the server routes
167
+ cache_key = generate_cache_key_from_uri request.fullpath
168
+
169
+ output = Rails.cache.fetch cache_key, expires: 12.hours, race_condition_ttl: 1.minute, namespace: :react_server do
170
+ RailsReactSSR::ServerRunner.exec!('server.js', props: {current_user: current_user, location: request.fullpath})
171
+ end
172
+
173
+ handle_server_response output
174
+ end
175
+
176
+ ```
177
+
178
+ ## Alternatives
179
+
180
+ There are several alternatives that are more comprehensive and might be a better fit for your use case:
181
+
182
+ 1) [ReactOnRails](https://github.com/shakacode/react_on_rails)
183
+ 2) [react-rails](https://github.com/reactjs/react-rails)
184
+ 3) [reactssr-rails](https://github.com/towry/reactssr-rails)
185
+
186
+ ## Issues
187
+
188
+ Report bugs at https://github.com/jefawks3/rails-react-ssr.
189
+ Please make sure to include how to reproduce the issue, otherwise it might be ignored.
190
+
191
+ ## Contributing
192
+
193
+ 1) Fork it (https://github.com/jefawks3/rails-react-ssr)
194
+ 2) Create your feature branch (git checkout -b my-new-feature)
195
+ 3) Commit your changes (git commit -am 'Add some feature')
196
+ 4) Push to the branch (git push origin my-new-feature)
197
+ 5) Create a new Pull Request
198
+
199
+ ## License
200
+
201
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
202
+
@@ -0,0 +1 @@
1
+ require 'rails_react_ssr'
@@ -0,0 +1,7 @@
1
+ require 'rails_react_ssr/errors'
2
+ require 'rails_react_ssr/webpacker_utils'
3
+ require 'rails_react_ssr/server_runner'
4
+
5
+ module RailsReactSSR
6
+
7
+ end
@@ -0,0 +1,24 @@
1
+ module RailsReactSSR
2
+ # RailsReactSSR error
3
+ class Error < StandardError
4
+ end
5
+
6
+ # Bundle errors
7
+ class BundleError < Error
8
+ attr_reader :bundle
9
+
10
+ def initialize(bundle, *args)
11
+ super *args
12
+
13
+ @bundle = bundle
14
+ end
15
+ end
16
+
17
+ # Missing bundle package
18
+ class MissingBundleError < BundleError
19
+ end
20
+
21
+ # Execution Error
22
+ class ExecutionError < BundleError
23
+ end
24
+ end
@@ -0,0 +1,120 @@
1
+ require 'webpacker'
2
+ require 'shellwords'
3
+ require 'active_support/json'
4
+ require 'open-uri'
5
+ require 'rails'
6
+ require 'open3'
7
+
8
+ module RailsReactSSR
9
+ ##
10
+ # Executes the ReactJS package using NodeJS that was built using webpacker
11
+ class ServerRunner
12
+ ##
13
+ # Redirect console output to be logged by an array
14
+ CONSOLE_POLYFILL = <<-CONSOLE_POLYFILL
15
+ const stdout = console.log;
16
+ const stderr = console.error;
17
+
18
+ const recordedLogs = [];
19
+
20
+ ['log', 'info', 'debug', 'warn', 'error'].forEach(level => {
21
+ console[level] = (...args) => {
22
+ recordedLogs.push({ level: level, args: args });
23
+ }
24
+ });
25
+ CONSOLE_POLYFILL
26
+
27
+ ##
28
+ # Execute the bundled package
29
+ #
30
+ # <tt>:props</tt> - The properties to pass to the server JS code
31
+ # <tt>:outputTemp</tt> - If true, output the compiled bundle to the tmp/ssr directory, pass a string to specify the
32
+ # output file
33
+ # <tt>:max_tries</tt> - The number of tries when getting the bundle from the webpack dev server
34
+ # <tt>:delay</tt> - The delay in ms between tries
35
+ def self.exec!(bundle, props: {}, outputTemp: false, max_tries: 10, delay: 1000)
36
+ bundle_file = RailsReactSSR::WebpackerUtils.open_bundle bundle, max_tries: max_tries, delay: delay
37
+
38
+ ## Format the properties for js
39
+ jsProps = props.inject({}) do |hash,(k,v)|
40
+ hash[k.to_s.camelcase.gsub(/\A./, &:downcase)] = v
41
+ hash
42
+ end
43
+
44
+ status = 0
45
+ output = nil
46
+
47
+ begin
48
+ js = Tempfile.new [File.basename(bundle_file, '.*'), File.extname(bundle_file)]
49
+
50
+ begin
51
+ write_console_polyfill js
52
+ write_props_polyfill js, jsProps
53
+ write_bundle js, bundle_file
54
+
55
+ js.flush
56
+
57
+ if outputTemp
58
+ outputTemp = Rails.root.join('tmp/ssr/', bundle) if outputTemp.is_a? TrueClass
59
+
60
+ Rails.logger.debug "Coping server bundle to #{outputTemp}"
61
+ IO.copy_stream js.path, outputTemp
62
+ end
63
+
64
+ status, output = exec_contents js
65
+ ensure
66
+ js.unlink
67
+ end
68
+ rescue => e
69
+ Rails.logger.error "Unable to execute the bundle '#{bundle}': #{e.message}"
70
+ raise RailsReactSSR::ExecutionError.new(bundle, "Unable to run the bundle '#{bundle}'")
71
+ ensure
72
+ bundle_file.close
73
+ end
74
+
75
+ raise RailsReactSSR::ExecutionError.new(bundle,"Unable to execute the server bundle #{bundle}") unless status.zero?
76
+
77
+ output
78
+ end
79
+
80
+ private
81
+
82
+ def self.exec_contents(file)
83
+ output = error = ''
84
+
85
+ cmd = ['node', Shellwords.escape(file.path)]
86
+
87
+ cmd_str = cmd.join ' '
88
+
89
+ status = Open3.popen3 cmd_str do |inp, out, err, thr|
90
+ output = out.read
91
+ error = err.read
92
+
93
+ Rails.logger.info "[#{thr.value.exitstatus}}] #{cmd_str}"
94
+ Rails.logger.debug output
95
+ Rails.logger.error error unless error.nil? || error.empty?
96
+
97
+ thr.value.exitstatus
98
+ end
99
+
100
+ [status, output]
101
+ end
102
+
103
+ def self.write_props_polyfill(temp_file, props)
104
+ temp_file.write <<-JS
105
+ const serverProps = #{ActiveSupport::JSON.encode props};
106
+
107
+ JS
108
+ end
109
+
110
+ def self.write_console_polyfill(temp_file)
111
+ temp_file.write CONSOLE_POLYFILL
112
+ temp_file.write "\n\n"
113
+ end
114
+
115
+ def self.write_bundle(temp_file, bundle_file)
116
+ IO.copy_stream bundle_file, temp_file
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,3 @@
1
+ module RailsReactSSR
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,73 @@
1
+ require 'uri'
2
+
3
+ module RailsReactSSR
4
+ class WebpackerUtils
5
+ ##
6
+ # Return the hashed name from the +bundle+
7
+ def self.hashed_bundle_name!(bundle)
8
+ Webpacker.manifest.lookup! bundle
9
+ rescue Webpacker::Manifest::MissingEntryError
10
+ raise RailsReactSSR::MissingBundleError.new(bundle, "The ReactJS package '#{bundle}' is missing from the manifest.json file.")
11
+ end
12
+
13
+ ##
14
+ # Open the +bundle+ file for reading
15
+ #
16
+ # Returns IO stream with the +bundle+ contents. If +bundle+ cannot be found,
17
+ # raises +RailsReactSSR::MissingBundleError+
18
+ def self.open_bundle(bundle, max_tries: 10, delay: 1000)
19
+ hashed = hashed_bundle_name! bundle
20
+
21
+ if Webpacker.dev_server.running?
22
+ dev_server_bundle hashed, max_tries, delay
23
+ else
24
+ local_file_bundle hashed
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def self.dev_bundle_uri(path)
31
+ URI::Generic.new(
32
+ Webpacker.dev_server.protocol,
33
+ nil,
34
+ Webpacker.dev_server.host,
35
+ Webpacker.dev_server.port,
36
+ nil,
37
+ path,
38
+ nil,
39
+ nil,
40
+ nil
41
+ ).to_s
42
+ end
43
+
44
+ def self.bundle_fullpath(path)
45
+ File.join Rails.root, 'public', path
46
+ end
47
+
48
+ def self.dev_server_bundle(hashed_bundle, max_tries, delay, tries = 0)
49
+ tries += 1
50
+
51
+ uri = self.dev_bundle_uri hashed_bundle
52
+
53
+ Rails.logger.debug "Reading remote bundle #{uri}"
54
+
55
+ open uri
56
+ rescue OpenURI::HTTPError => e
57
+ # On the first page hit my not be available on the dev server so we need to wait for it to compile
58
+ if tries < max_tries
59
+ Rails.logger.debug "The remote bundle is not ready trying again in #{delay}ms - #{tries} of #{max_tries}"
60
+ sleep delay / 1000
61
+ retry
62
+ else
63
+ raise e
64
+ end
65
+ end
66
+
67
+ def self.local_file_bundle(hashed_bundle)
68
+ full_path = File.join Rails.root, 'public', hashed_bundle
69
+
70
+ File.open full_path, 'rb'
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+ class RailsReactSSR::ServerRunnerTest < RailsReactSSR::Test
4
+ def setup
5
+ # Do nothing
6
+ end
7
+
8
+ def teardown
9
+ # Do nothing
10
+ end
11
+
12
+ def test_application_temp_output
13
+ tempFile = File.expand_path 'tmp/output.js'
14
+
15
+ File.unlink tempFile if File.exists? tempFile
16
+
17
+ RailsReactSSR::ServerRunner.exec! 'application.js', outputTemp: tempFile
18
+
19
+ assert_equal File.read(tempFile), <<-OUTPUT
20
+ const stdout = console.log;
21
+ const stderr = console.error;
22
+
23
+ const recordedLogs = [];
24
+
25
+ ['log', 'info', 'debug', 'warn', 'error'].forEach(level => {
26
+ console[level] = (...args) => {
27
+ recordedLogs.push({ level: level, args: args });
28
+ }
29
+ });
30
+
31
+
32
+ const serverProps = {};
33
+
34
+ console.log('Hello World from Webpacker');
35
+
36
+ stdout('<html><body>Hello from the server</body></html>');
37
+ OUTPUT
38
+ end
39
+
40
+ def test_application_output
41
+ output = RailsReactSSR::ServerRunner.exec! 'application.js'
42
+
43
+ assert_equal output, <<-OUTPUT
44
+ <html><body>Hello from the server</body></html>
45
+ OUTPUT
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "config/application"
2
+
3
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ console.log('Hello World from Webpacker');
2
+
3
+ stdout('<html><body>Hello from the server</body></html>');
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4
+ ENV["NODE_ENV"] ||= ENV["RAILS_ENV"]
5
+
6
+ require "pathname"
7
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8
+ Pathname.new(__FILE__).realpath)
9
+
10
+ require "bundler/setup"
11
+
12
+ require "webpacker"
13
+ require "webpacker/webpack_runner"
14
+ Webpacker::WebpackRunner.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4
+ ENV["NODE_ENV"] ||= ENV["RAILS_ENV"]
5
+
6
+ require "pathname"
7
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8
+ Pathname.new(__FILE__).realpath)
9
+
10
+ require "bundler/setup"
11
+
12
+ require "webpacker"
13
+ require "webpacker/dev_server_runner"
14
+ Webpacker::DevServerRunner.run(ARGV)
@@ -0,0 +1,5 @@
1
+ # This file allows the `Rails.root` to be correctly determined.
2
+
3
+ require_relative "config/environment"
4
+
5
+ run Rails.application
@@ -0,0 +1,12 @@
1
+ require "action_controller/railtie"
2
+ require "action_view/railtie"
3
+ require "webpacker"
4
+
5
+ module TestApp
6
+ class Application < ::Rails::Application
7
+ config.secret_key_base = "abcdef"
8
+ config.eager_load = true
9
+ config.webpacker.check_yarn_integrity = false
10
+ config.active_support.test_order = :sorted
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ require_relative "application"
2
+
3
+ Rails.backtrace_cleaner.remove_silencers!
4
+ Rails.application.initialize!
File without changes
@@ -0,0 +1,97 @@
1
+ # Note: You must restart bin/webpack-dev-server for changes to take effect
2
+
3
+ default: &default
4
+ source_path: app/javascript
5
+ source_entry_path: packs
6
+ public_root_path: public
7
+ public_output_path: packs
8
+ cache_path: tmp/cache/webpacker
9
+ webpack_compile_output: false
10
+
11
+ # Additional paths webpack should lookup modules
12
+ # ['app/assets', 'engine/foo/app/assets']
13
+ resolved_paths:
14
+ - app/assets
15
+ - /etc/yarn
16
+
17
+ # Reload manifest.json on all requests so we reload latest compiled packs
18
+ cache_manifest: false
19
+
20
+ # Extract and emit a css file
21
+ extract_css: false
22
+
23
+ static_assets_extensions:
24
+ - .jpg
25
+ - .jpeg
26
+ - .png
27
+ - .gif
28
+ - .tiff
29
+ - .ico
30
+ - .svg
31
+
32
+ extensions:
33
+ - .mjs
34
+ - .js
35
+ - .sass
36
+ - .scss
37
+ - .css
38
+ - .module.sass
39
+ - .module.scss
40
+ - .module.css
41
+ - .png
42
+ - .svg
43
+ - .gif
44
+ - .jpeg
45
+ - .jpg
46
+
47
+ development:
48
+ <<: *default
49
+ compile: true
50
+
51
+ # Reference: https://webpack.js.org/configuration/dev-server/
52
+ dev_server:
53
+ https: false
54
+ host: localhost
55
+ port: 3035
56
+ public: localhost:3035
57
+ hmr: false
58
+ # Inline should be set to true if using HMR
59
+ inline: true
60
+ overlay: true
61
+ disable_host_check: true
62
+ use_local_ip: false
63
+ pretty: false
64
+
65
+ test:
66
+ <<: *default
67
+ compile: true
68
+
69
+ # Compile test packs to a separate directory
70
+ public_output_path: packs-test
71
+
72
+ production:
73
+ <<: *default
74
+
75
+ # Production depends on precompilation of packs prior to booting for performance.
76
+ compile: false
77
+
78
+ # Extract and emit a css file
79
+ extract_css: true
80
+
81
+ # Cache manifest.json for performance
82
+ cache_manifest: true
83
+
84
+ staging:
85
+ <<: *default
86
+
87
+ # Production depends on precompilation of packs prior to booting for performance.
88
+ compile: false
89
+
90
+ # Extract and emit a css file
91
+ extract_css: true
92
+
93
+ # Cache manifest.json for performance
94
+ cache_manifest: true
95
+
96
+ # Compile staging packs to a separate directory
97
+ public_output_path: packs-staging
@@ -0,0 +1,19 @@
1
+ # Note: You must restart bin/webpack-dev-server for changes to take effect
2
+
3
+ default: &default
4
+ public_root_path: ../public
5
+
6
+ development:
7
+ <<: *default
8
+ compile: true
9
+
10
+ test:
11
+ <<: *default
12
+ compile: true
13
+ public_output_path: packs-test
14
+
15
+ production:
16
+ <<: *default
17
+ compile: false
18
+ extract_css: true
19
+ cache_manifest: true
@@ -0,0 +1,12 @@
1
+ The remote bundle is not ready trying again in 1000ms - 1 of 10
2
+ The remote bundle is not ready trying again in 1000ms - 1 of 10
3
+ The remote bundle is not ready trying again in 1000ms - 1 of 10
4
+ The remote bundle is not ready trying again in 1000ms - 1 of 10
5
+ Reading remote bundle http://localhost:3035/packs/application-k344a6d59eef8632c9d1.js
6
+ The remote bundle is not ready trying again in 1000ms - 1 of 10
7
+ Reading remote bundle http://localhost:3035/packs/application-k344a6d59eef8632c9d1.js
8
+ The remote bundle is not ready trying again in 1000ms - 1 of 10
9
+ [Webpacker] Compiling…
10
+ [Webpacker] Compilation failed:
11
+ error Command "webpack" not found.
12
+
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "test_app",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "license": "MIT",
6
+ "private": true,
7
+ "dependencies": {
8
+ "left-pad": "^1.2.0"
9
+ },
10
+ "devDependencies": {
11
+ "right-pad": "^1.0.1"
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ console.log('Hello World from Webpacker');
2
+
3
+ stdout('<html><body>Hello from the server</body></html>');
@@ -0,0 +1,31 @@
1
+ {
2
+ "bootstrap.css": "/packs/bootstrap-c38deda30895059837cf.css",
3
+ "application.css": "/packs/application-dd6b1cd38bfa093df600.css",
4
+ "bootstrap.js": "/packs/bootstrap-300631c4f0e0f9c865bc.js",
5
+ "application.js": "/packs/application-k344a6d59eef8632c9d1.js",
6
+ "application.png": "/packs/application-k344a6d59eef8632c9d1.png",
7
+ "fonts/fa-regular-400.woff2": "/packs/fonts/fa-regular-400-944fb546bd7018b07190a32244f67dc9.woff2",
8
+ "media/images/image.jpg": "/packs/media/images/image-c38deda30895059837cf.jpg",
9
+ "media/images/nested/image.jpg": "/packs/media/images/nested/image-c38deda30895059837cf.jpg",
10
+ "media/images/mb-icon.png": "/packs/media/images/mb-icon-c38deda30895059837cf.png",
11
+ "media/images/nested/mb-icon.png": "/packs/media/images/nested/mb-icon-c38deda30895059837cf.png",
12
+ "entrypoints": {
13
+ "application": {
14
+ "js": [
15
+ "/packs/vendors~application~bootstrap-c20632e7baf2c81200d3.chunk.js",
16
+ "/packs/vendors~application-e55f2aae30c07fb6d82a.chunk.js",
17
+ "/packs/application-k344a6d59eef8632c9d1.js"
18
+ ],
19
+ "css": [
20
+ "/packs/1-c20632e7baf2c81200d3.chunk.css",
21
+ "/packs/application-k344a6d59eef8632c9d1.chunk.css"
22
+ ]
23
+ },
24
+ "hello_stimulus": {
25
+ "css": [
26
+ "/packs/1-c20632e7baf2c81200d3.chunk.css",
27
+ "/packs/hello_stimulus-k344a6d59eef8632c9d1.chunk.css"
28
+ ]
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,11 @@
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ left-pad@^1.2.0:
6
+ version "1.2.0"
7
+ resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.2.0.tgz#d30a73c6b8201d8f7d8e7956ba9616087a68e0ee"
8
+
9
+ right-pad@^1.0.1:
10
+ version "1.0.1"
11
+ resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0"
@@ -0,0 +1,38 @@
1
+ require 'minitest/autorun'
2
+
3
+ require "rails"
4
+ require "rails/test_help"
5
+ require "byebug"
6
+ require 'rails-react-ssr'
7
+
8
+ require_relative "test_app/config/environment"
9
+
10
+ Rails.env = "production"
11
+
12
+ Rails.logger = Logger.new(STDOUT)
13
+ Rails.logger.level = Logger::DEBUG
14
+
15
+
16
+ Webpacker.instance = ::Webpacker::Instance.new
17
+
18
+ class RailsReactSSR::Test < Minitest::Test
19
+ private
20
+ def reloaded_config
21
+ Webpacker.instance.instance_variable_set(:@env, nil)
22
+ Webpacker.instance.instance_variable_set(:@config, nil)
23
+ Webpacker.instance.instance_variable_set(:@dev_server, nil)
24
+ Webpacker.env
25
+ Webpacker.config
26
+ Webpacker.dev_server
27
+ end
28
+
29
+ def with_rails_env(env)
30
+ original = Rails.env
31
+ Rails.env = ActiveSupport::StringInquirer.new(env)
32
+ reloaded_config
33
+ yield
34
+ ensure
35
+ Rails.env = ActiveSupport::StringInquirer.new(original)
36
+ reloaded_config
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+ require 'webpacker/dev_server_runner'
3
+
4
+ class RailsReactSSR::WebpackerUtilsTest < RailsReactSSR::Test
5
+ def test_bundle_not_found!
6
+ error = assert_raises RailsReactSSR::MissingBundleError do
7
+ RailsReactSSR::WebpackerUtils.hashed_bundle_name! 'missing.js'
8
+ end
9
+
10
+ assert_match 'missing.js', error.bundle
11
+ assert_match "The ReactJS package 'missing.js' is missing from the manifest.json file.", error.message
12
+ end
13
+
14
+ def test_bundle_found!
15
+ assert_equal RailsReactSSR::WebpackerUtils.hashed_bundle_name!('application.js'),
16
+ "/packs/application-k344a6d59eef8632c9d1.js"
17
+ end
18
+
19
+ def test_open_local_file
20
+ io = RailsReactSSR::WebpackerUtils.open_bundle 'application.js'
21
+
22
+ refute Webpacker.dev_server.running?
23
+
24
+ assert_equal io.read, raw_application_js
25
+ end
26
+
27
+ def test_open_remote_file
28
+ # TODO Run dev server during tests to make sure remote file is accessible
29
+ skip 'Need to find a way to run the dev server during the tests'
30
+ end
31
+
32
+ private
33
+
34
+ def raw_application_js
35
+ <<-AppplicaitonJS
36
+ console.log('Hello World from Webpacker');
37
+
38
+ stdout('<html><body>Hello from the server</body></html>');
39
+ AppplicaitonJS
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-react-ssr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - James Fawks
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: webpacker
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 5.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 5.2.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.14'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '11.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '11.1'
97
+ description:
98
+ email:
99
+ - jefawks3@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - CHANGELOG.md
105
+ - README.md
106
+ - lib/rails-react-ssr.rb
107
+ - lib/rails_react_ssr.rb
108
+ - lib/rails_react_ssr/errors.rb
109
+ - lib/rails_react_ssr/server_runner.rb
110
+ - lib/rails_react_ssr/version.rb
111
+ - lib/rails_react_ssr/webpacker_utils.rb
112
+ - test/server_runner_test.rb
113
+ - test/test_app/Rakefile
114
+ - test/test_app/app/javascript/packs/application.js
115
+ - test/test_app/bin/webpack
116
+ - test/test_app/bin/webpack-dev-server
117
+ - test/test_app/config.ru
118
+ - test/test_app/config/application.rb
119
+ - test/test_app/config/environment.rb
120
+ - test/test_app/config/webpack/development.js
121
+ - test/test_app/config/webpacker.yml
122
+ - test/test_app/config/webpacker_public_root.yml
123
+ - test/test_app/log/development.log
124
+ - test/test_app/package.json
125
+ - test/test_app/public/packs/application-k344a6d59eef8632c9d1.js
126
+ - test/test_app/public/packs/manifest.json
127
+ - test/test_app/yarn.lock
128
+ - test/test_helper.rb
129
+ - test/webpacker_utils_test.rb
130
+ homepage: https://github.com/jefawks3/rails-react-ssr
131
+ licenses:
132
+ - MIT
133
+ metadata:
134
+ homepage_uri: https://github.com/jefawks3/rails-react-ssr
135
+ source_code_uri: https://github.com/jefawks3/rails-react-ssr
136
+ changelog_uri: https://github.com/jefawks3/rails-react-ssr/blob/master/CHANGELOG.md
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 2.2.0
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.0.6
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Light weight React SSR (Server Side Rendering) integration for Ruby on Rails,
156
+ Webpacker and NodeJS
157
+ test_files:
158
+ - test/test_app/app/javascript/packs/application.js
159
+ - test/test_app/bin/webpack
160
+ - test/test_app/bin/webpack-dev-server
161
+ - test/test_app/config/environment.rb
162
+ - test/test_app/config/webpacker_public_root.yml
163
+ - test/test_app/config/application.rb
164
+ - test/test_app/config/webpack/development.js
165
+ - test/test_app/config/webpacker.yml
166
+ - test/test_app/config.ru
167
+ - test/test_app/Rakefile
168
+ - test/test_app/yarn.lock
169
+ - test/test_app/public/packs/manifest.json
170
+ - test/test_app/public/packs/application-k344a6d59eef8632c9d1.js
171
+ - test/test_app/package.json
172
+ - test/test_app/log/development.log
173
+ - test/server_runner_test.rb
174
+ - test/webpacker_utils_test.rb
175
+ - test/test_helper.rb