rails-react-ssr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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