humid 0.0.1 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 331e268f4bfc9f9b46e4246a9fb1af56245616bedecfb13a9b220040fca74636
4
- data.tar.gz: 87631778cddbd5511202a0a3aaaa54fb6827b8aee8bdae29f80297ee6764cd55
3
+ metadata.gz: a783b726d5542a0abfc94fc1918aad0212c13ac7b6db8b8efd90c5452a222b2e
4
+ data.tar.gz: b9c3719ace7b00d210c992b48f9c4ee02010e81d19c30aa47e8bf6fdd9e9373b
5
5
  SHA512:
6
- metadata.gz: 5161834e3f6b0a8d6cb9e991965df392491e204373c0ec54773cf114f8c489be88f435a5d6ba90c226dc6a698ea053ca3ccaf64581cb01399272057ecb600990
7
- data.tar.gz: a545a0ffb5c7f1dfdf333bb960566a28fb355fd3dfc60ede7ed88f3210f507ca21e7b389138c07b2a773903110df66b7e60df844a57815c0a87fb624a15ee44c
6
+ metadata.gz: d29027f962022c6f8d5aeaf09c11730d572a32f8936e025fc04c3490921ac51468f6567e99f29aeb090a344150fa5edce2bbe14e83c18bfe9afc62e89fb34a87
7
+ data.tar.gz: 9ceb09c4bebef88f71f3b9c229c9317526a1d5cd5100dce4a270aad383334401a14baa7e108417170c5c3cad122523e9539733404d4c9a3cdf6f7bb8c2ec5dbf
data/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # Humid
2
-
3
- Humid is a lightweight helper that leans on [mini_racer] and [webpacker] to
4
- generate Server Side Rendered (SSR) pages from your javascript application.
5
-
6
2
  [![Build
7
3
  Status](https://circleci.com/gh/thoughtbot/humid.svg?style=shield)](https://circleci.com/gh/thoughtbot/humid)
8
4
 
5
+ Humid is a lightweight wrapper around [mini_racer] and [webpacker] used to
6
+ generate Server Side Rendered (SSR) pages from your javascript application.
7
+ While it was built for React, it can work with any JS function that returns a
8
+ HTML string.
9
+
9
10
  ## Caution
10
11
 
11
12
  This project is in its early phases of development. Its interface,
@@ -33,30 +34,51 @@ Add an initializer to configure
33
34
 
34
35
  ```ruby
35
36
  Humid.configure do |config|
36
- # name of your webpacker pack. Defaults to "server_rendering.js"
37
- config.server_rendering_source = "server_rendering.js"
38
-
39
- # name of your webpacker pack source map. Defaults to `nil`
40
- config.server_rendering_source_map = "server_rendering.js.map"
41
-
42
- # The logger instance. Defaults to `Logger.new(STDOUT)`
37
+ # Name of your webpacker pack file located in `app/javascript/packs/`. You
38
+ # should use a separate pack from your `application.js`.
39
+ #
40
+ # Defaults to "server_rendering.js"
41
+ config.server_rendering_pack = "server_rendering.js"
42
+
43
+ # Name of your webpacker pack source map.
44
+ #
45
+ # Defaults to `false`
46
+ config.use_source_map = true
47
+
48
+ # Raise errors if JS rendering failed. If false, the error will be
49
+ # logged out to Rails log and Humid.render will return an empty string
50
+ #
51
+ # Defaults to true.
52
+ config.raise_render_errors = Rails.env.development? || Rails.env.test?
53
+
54
+ # The logger instance.
43
55
  # `console.log` and friends (`warn`, `error`) are delegated to
44
56
  # the respective logger levels on the ruby side.
57
+ #
58
+ # Defaults to `Logger.new(STDOUT)`
45
59
  config.logger = Rails.logger
46
60
 
47
- # context_options. Options passed to mini_racer. Defaults to
48
- # config.context_options = {
49
- # timeout: 1000,
50
- # ensure_gc_after_idle: 2000
51
- # }
61
+ # Options passed to mini_racer.
62
+ #
63
+ # Defaults to empty `{}`.
64
+ config.context_options = {
65
+ timeout: 1000,
66
+ ensure_gc_after_idle: 2000
67
+ }
52
68
  end
53
69
 
54
- # If using puma in single mode
55
- # Humid.create_context
70
+ # Common development options
71
+ if Rails.env.development? || Rails.env.test?
72
+ # Use single_threaded mode for Spring and other forked envs.
73
+ MiniRacer::Platform.set_flags! :single_threaded
74
+
75
+ # If you're using Puma in single mode:
76
+ Humid.create_context
77
+ end
56
78
  ```
57
79
 
58
80
  If you'd like support for source map support, you will need to
59
- 1. Ensure `config.server_rendering_source_map` has a value
81
+ 1. Ensure `config.use_source_map` is set to `true`
60
82
  2. Add the following to your `server_rendering.js` pack.
61
83
 
62
84
  ```javascript
@@ -72,6 +94,8 @@ require("source-map-support").install({
72
94
 
73
95
  ## The mini_racer environment.
74
96
 
97
+ ### Functions not available
98
+
75
99
  The following functions are **not** available in the mini_racer environment
76
100
 
77
101
  - `setTimeout`
@@ -86,11 +110,59 @@ The following functions are **not** available in the mini_racer environment
86
110
  `console.log` and friends (`info`, `error`, `warn`) are delegated to the
87
111
  respective methods on the configured logger.
88
112
 
89
- ### Webpacker
113
+ ## Usage
114
+
115
+ Pass your HTML render function to `setHumidRenderer`
116
+
117
+ ```javascript
118
+ // Set a factory function that will create a new instance of our app
119
+ // for each request.
120
+ setHumidRenderer((json) => {
121
+ const initialState = JSON.parse(json)
122
+
123
+ return ReactDOMServer.renderToString(
124
+ <Application initialPage={initialState}/>
125
+ )
126
+ })
127
+ ```
128
+
129
+ And finally call `render` from ERB.
130
+
131
+ ```ruby
132
+ <%= Humid.render(initial_state) %>
133
+ ```
134
+
135
+ Instrumentation is included:
136
+
137
+ ```
138
+ Completed 200 OK in 14ms (Views: 0.2ms | Humid SSR: 11.0ms | ActiveRecord: 2.7ms)
139
+ ```
140
+
141
+ ### Puma
142
+
143
+ `mini_racer` is thread safe, but not fork safe. To use with web servers that
144
+ employ forking, use `Humid.create_context` only on forked processes.
145
+
146
+ ```ruby
147
+ # Puma
148
+ on_worker_boot do
149
+ Humid.create_context
150
+ end
151
+
152
+ on_worker_shutdown do
153
+ Humid.dispose
154
+ end
155
+ ```
156
+
157
+ ### Server-side libraries that detect node.js envs.
90
158
  You may need webpacker to create aliases for server friendly libraries that can
91
159
  not detect the `mini_racer` environment.
92
160
 
93
161
  ```diff
162
+ // config/webpack/production.js
163
+ // config/webpack/development.js
164
+ // config/webpack/test.js
165
+
94
166
  process.env.NODE_ENV = process.env.NODE_ENV || 'development'
95
167
 
96
168
  const environment = require('./environment')
@@ -117,39 +189,32 @@ not detect the `mini_racer` environment.
117
189
  +module.exports = [ssrConfig, webConfig]
118
190
  ```
119
191
 
120
- ## Usage
121
-
122
- Pass your HTML render function to `setHumidRenderer`
123
-
124
- ```javascript
125
- setHumidRenderer((json) => {
126
- const initialState = JSON.parse(json)
127
- return ReactDOMServer.renderToString(
128
- <Application initialPage={initialState}/>
129
- )
130
- })
131
- ```
192
+ ## Writing universal code
193
+ [Vue has an amazing resource][vue_ssr] on how to write universal code. Below
194
+ are a few highlights that are important to keep in mind.
132
195
 
133
- And finally call `render` from ERB.
196
+ ### State
134
197
 
135
- ```ruby
136
- <%= Humid.render(initial_state) %>
137
- ```
198
+ Humid uses a single context across multiple request. To avoid state pollution, we
199
+ provide a factory function to `setHumidRenderer` that builds a new app instance on
200
+ every call.
138
201
 
139
- ### Puma
202
+ This provides better isolation, but as it is still a shared context, polluting
203
+ `global` is still possible. Be careful of modifying `global` in your code.
140
204
 
141
- `mini_racer` is thread safe, but not fork safe. To use with web servers that
142
- employ forking, use `Humid.create_context` only on forked processes.
205
+ ### Polyfills
143
206
 
144
- ```ruby
145
- # Puma
146
- on_worker_boot do
147
- Humid.create_context
148
- end
207
+ Polyfills will fail when using in the `mini_racer` environment because of missing
208
+ browser APIs. Account for this by moving the `require` to `componentDidMount`
209
+ in your component.
149
210
 
150
- on_worker_shutdown do
151
- Humid.dispose
152
- end
211
+ ```
212
+ componentDidMount() {
213
+ const dialogPolyfill = require('dialog-polyfill').default
214
+ dialogPolyfill.registerDialog(this.dialog.current)
215
+ this.dialog.current.open = this.props.open
216
+ this.dialog.current.showModal()
217
+ }
153
218
  ```
154
219
 
155
220
  ## Contributing
@@ -177,3 +242,4 @@ See [our other projects][community] or
177
242
  [hire]: https://thoughtbot.com?utm_source=github
178
243
  [mini_racer]: https://github.com/rubyjs/mini_racer
179
244
  [webpacker]: https://github.com/rails/webpacker
245
+ [vue_ssr]: https://ssr.vuejs.org/
data/lib/humid.rb CHANGED
@@ -11,12 +11,24 @@ module Humid
11
11
  extend self
12
12
  include ActiveSupport::Configurable
13
13
 
14
- config_accessor :server_rendering_file do
14
+ class RenderError < StandardError
15
+ end
16
+
17
+ class FileNotFound < StandardError
18
+ end
19
+
20
+ @@context = nil
21
+
22
+ config_accessor :server_rendering_pack do
15
23
  "server_rendering.js"
16
24
  end
17
25
 
18
- config_accessor :server_rendering_source_map do
19
- nil
26
+ config_accessor :use_source_map do
27
+ false
28
+ end
29
+
30
+ config_accessor :raise_render_errors do
31
+ true
20
32
  end
21
33
 
22
34
  config_accessor :logger do
@@ -24,10 +36,7 @@ module Humid
24
36
  end
25
37
 
26
38
  config_accessor :context_options do
27
- {
28
- timeout: 1000,
29
- ensure_gc_after_idle: 2000
30
- }
39
+ {}
31
40
  end
32
41
 
33
42
  def remove_functions
@@ -54,16 +63,30 @@ module Humid
54
63
  JS
55
64
  end
56
65
 
57
- def context
58
- if @@context && Webpacker.env.development? && Webpacker.compiler.stale?
66
+ def handle_stale_files
67
+ if Webpacker.compiler.stale?
59
68
  Webpacker.compiler.compile
69
+ end
70
+
71
+ public_path = Webpacker.config.public_path
72
+ server_rendering_pack = config.server_rendering_pack
73
+ source_path = public_path.join(Webpacker.manifest.lookup(server_rendering_pack)[1..-1])
74
+ filename = File.basename(source_path.to_s)
75
+
76
+ if @@current_filename != filename
60
77
  dispose
61
78
  create_context
62
- else
63
- @@context
64
79
  end
65
80
  end
66
81
 
82
+ def context
83
+ if @@context && Webpacker.env.development?
84
+ handle_stale_files
85
+ end
86
+
87
+ @@context
88
+ end
89
+
67
90
  def dispose
68
91
  if @@context
69
92
  @@context.dispose
@@ -84,21 +107,39 @@ module Humid
84
107
  ctx.eval(js)
85
108
 
86
109
  public_path = Webpacker.config.public_path
87
- asset_path = public_path.join(Webpacker.manifest.lookup(config.server_rendering_file)[1..-1])
88
- filename = File.basename(asset_path.to_s)
89
- ctx.eval(File.read(asset_path), filename: filename)
90
110
 
91
- if config.server_rendering_source_map
92
- map_path = public_path.join(Webpacker.manifest.lookup(config.server_rendering_source_map)[1..-1])
111
+ webpack_source_file = Webpacker.manifest.lookup(config.server_rendering_pack)
112
+ if webpack_source_file.nil?
113
+ raise FileNotFound.new("Humid could not find a built pack for #{config.server_rendering_pack}")
114
+ end
115
+
116
+ if config.use_source_map
117
+ webpack_source_map = Webpacker.manifest.lookup("#{config.server_rendering_pack}.map")
118
+ map_path = public_path.join(webpack_source_map[1..-1])
93
119
  ctx.attach("readSourceMap", proc { File.read(map_path) })
94
120
  end
95
121
 
122
+ source_path = public_path.join(webpack_source_file[1..-1])
123
+ filename = File.basename(source_path.to_s)
124
+ @@current_filename = filename
125
+ ctx.eval(File.read(source_path), filename: filename)
126
+
96
127
  @@context = ctx
97
128
  end
98
129
 
99
130
  def render(*args)
100
131
  ActiveSupport::Notifications.instrument("render.humid") do
101
- @@context.call("__renderer", *args)
132
+ context.call("__renderer", *args)
133
+ rescue MiniRacer::RuntimeError => e
134
+ message = ([e.message] + e.backtrace.filter {|x| x.starts_with? "JavaScript"}).join("\n")
135
+ render_error = Humid::RenderError.new(message)
136
+
137
+ if config.raise_render_errors
138
+ raise render_error
139
+ else
140
+ config.logger.error(render_error.inspect)
141
+ ""
142
+ end
102
143
  end
103
144
  end
104
145
  end
data/lib/humid/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Humid
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.5".freeze
3
3
  end
@@ -45,7 +45,7 @@ RSpec.describe Humid::LogSubscriber do
45
45
  end
46
46
 
47
47
  it "is attached" do
48
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
48
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
49
49
  Humid.create_context
50
50
  expect(Humid::LogSubscriber.runtime).to eql(0)
51
51
  Humid.render
data/spec/render_spec.rb CHANGED
@@ -14,14 +14,24 @@ RSpec.describe "Humid" do
14
14
  end
15
15
 
16
16
  it "creates a context with initial js" do
17
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
17
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
18
18
  Humid.create_context
19
19
 
20
20
  expect(Humid.context).to be_kind_of(MiniRacer::Context)
21
21
  end
22
22
 
23
+ context "When the file can not be found" do
24
+ it "raises" do
25
+ allow(Humid.config).to receive("server_rendering_pack") { "does_not_exist.js" }
26
+
27
+ expect {
28
+ Humid.create_context
29
+ }.to raise_error(Humid::FileNotFound, "Humid could not find a built pack for does_not_exist.js")
30
+ end
31
+ end
32
+
23
33
  it "does not have timeouts, immediates, and intervals" do
24
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
34
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
25
35
 
26
36
  Humid.create_context
27
37
 
@@ -43,7 +53,7 @@ RSpec.describe "Humid" do
43
53
  end
44
54
 
45
55
  it "proxies to Rails logger" do
46
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
56
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
47
57
  Humid.create_context
48
58
  expect(Humid.logger).to receive(:info).with("hello")
49
59
 
@@ -53,7 +63,7 @@ RSpec.describe "Humid" do
53
63
 
54
64
  describe "context" do
55
65
  it "returns the created context" do
56
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
66
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
57
67
 
58
68
  Humid.create_context
59
69
 
@@ -64,7 +74,7 @@ RSpec.describe "Humid" do
64
74
  it "does not recompile the JS" do
65
75
  allow(Webpacker).to receive_message_chain("env.development?") { false }
66
76
  allow(Webpacker).to receive_message_chain("compiler.stale?") { true }
67
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
77
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
68
78
 
69
79
  Humid.create_context
70
80
  prev_context = Humid.context
@@ -79,37 +89,61 @@ RSpec.describe "Humid" do
79
89
  end
80
90
  end
81
91
 
82
- context "when the js is stale and env is development" do
83
- it "compiles the JS" do
92
+ context "when the env is development" do
93
+ it "compiles the JS when stale" do
84
94
  allow(Webpacker).to receive_message_chain("env.development?") { true }
85
95
  allow(Webpacker).to receive_message_chain("compiler.stale?") { true }
86
96
  allow(Webpacker).to receive_message_chain("compiler.compile")
87
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
97
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
88
98
 
89
99
  Humid.create_context
90
100
  prev_context = Humid.context
91
101
  expect(prev_context).to be_kind_of(MiniRacer::Context)
92
102
 
93
103
  allow(Webpacker).to receive_message_chain("compiler.stale?") { true }
104
+ # This simulates a changing file
105
+ allow(Humid.config).to receive("server_rendering_pack") { "simple_changed.js" }
106
+
107
+ next_context = Humid.context
108
+
109
+ expect(prev_context).to_not eql(next_context)
110
+ expect(next_context).to be_kind_of(MiniRacer::Context)
111
+ end
112
+
113
+ it "creates a new context when webpack-devserver already handled JS staleness" do
114
+ allow(Webpacker).to receive_message_chain("env.development?") { true }
115
+ allow(Webpacker).to receive_message_chain("compiler.stale?") { true }
116
+ allow(Webpacker).to receive_message_chain("compiler.compile")
117
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
118
+
119
+ Humid.create_context
120
+ prev_context = Humid.context
121
+ expect(Humid.render).to eql("hello")
122
+ expect(prev_context).to be_kind_of(MiniRacer::Context)
123
+
124
+ allow(Webpacker).to receive_message_chain("compiler.stale?") { false }
125
+ # This simulates a changing file
126
+ allow(Humid.config).to receive("server_rendering_pack") { "simple_changed.js" }
94
127
 
95
128
  next_context = Humid.context
96
129
 
97
130
  expect(prev_context).to_not eql(next_context)
98
131
  expect(next_context).to be_kind_of(MiniRacer::Context)
132
+ expect(Humid.render).to eql("hello changed")
99
133
  end
100
134
  end
101
135
  end
102
136
 
103
137
  describe "render" do
104
138
  it "returns a js output" do
105
- allow(Humid.config).to receive("server_rendering_file") { "simple.js" }
139
+ allow(Humid.config).to receive("server_rendering_pack") { "simple.js" }
106
140
  Humid.create_context
107
141
 
108
142
  expect(Humid.render).to eql("hello")
109
143
  end
110
144
 
111
145
  it "applys args to the func" do
112
- allow(Humid.config).to receive("server_rendering_file") { "args.js" }
146
+ allow(Humid.config).to receive("server_rendering_pack") { "args.js" }
113
147
  Humid.create_context
114
148
 
115
149
  args = ["a", 1, 2, [], {}]
@@ -118,22 +152,37 @@ RSpec.describe "Humid" do
118
152
  end
119
153
 
120
154
  it "can use source maps to see errors" do
121
- allow(Humid.config).to receive("server_rendering_file") { "reporting.js" }
122
- allow(Humid.config).to receive("server_rendering_source_map") {
123
- "reporting.js.map"
124
- }
155
+ allow(Humid.config).to receive("server_rendering_pack") { "reporting.js" }
156
+ allow(Humid.config).to receive("use_source_map") { true }
125
157
 
126
158
  Humid.create_context
127
159
 
128
160
  expect {
129
161
  Humid.render
130
162
  }.to raise_error { |error|
131
- expect(error).to be_a(MiniRacer::RuntimeError)
132
- expect(error.message).to eql("Error: ^^ Look! These stack traces map to the actual source code :)")
133
- expect(error.backtrace[0]).to eql("JavaScript at throwSomeError (/webpack:/app/javascript/packs/components/error-causing-component.js:2:1)")
134
- expect(error.backtrace[1]).to eql("JavaScript at ErrorCausingComponent (/webpack:/app/javascript/packs/components/error-causing-component.js:8:1)")
135
- expect(error.backtrace[2]).to eql("JavaScript at /webpack:/app/javascript/packs/reporting.js:18:1")
163
+ expect(error).to be_a(Humid::RenderError)
164
+ message = <<~MSG
165
+ Error: ^^ Look! These stack traces map to the actual source code :)
166
+ JavaScript at throwSomeError (/webpack:/app/javascript/packs/components/error-causing-component.js:2:1)
167
+ JavaScript at ErrorCausingComponent (/webpack:/app/javascript/packs/components/error-causing-component.js:8:1)
168
+ JavaScript at /webpack:/app/javascript/packs/reporting.js:18:1
169
+ MSG
170
+
171
+ expect(error.message).to eql message.strip
136
172
  }
137
173
  end
174
+
175
+ it "siliences render errors to the log" do
176
+ allow(Humid.config).to receive("server_rendering_pack") { "reporting.js" }
177
+ allow(Humid.config).to receive("raise_render_errors") { false }
178
+ allow(Humid.config).to receive("use_source_map") { true }
179
+
180
+ Humid.create_context
181
+
182
+ expect(Humid.logger).to receive(:error)
183
+ output = Humid.render
184
+
185
+ expect(output).to eql("")
186
+ end
138
187
  end
139
188
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: humid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johny Ho
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-10 00:00:00.000000000 Z
11
+ date: 2021-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webpacker
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '6.1'
47
+ version: '6.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '6.1'
54
+ version: '6.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '6.1'
103
+ version: '6.0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '6.1'
110
+ version: '6.0'
111
111
  description: Javascript SSR rendering for Rails
112
112
  email: jho406@gmail.com
113
113
  executables: []