rack-spa 0.10.0 → 0.11.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 +4 -4
- data/lib/rack/dynamic_config_writer.rb +24 -9
- data/lib/rack/spa_app.rb +20 -5
- data/lib/rack/spa_rewrite.rb +13 -13
- data/lib/rack_spa.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df310728de6bc267ad3d202364d7500429e18cdc2d9e14f96de887b6bbeae47d
|
4
|
+
data.tar.gz: 8347024dc1cf3a84b1b880687680e14f1299915a0ec60c83af466e1c7f2fc86c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1393398e04629cd0f7af9d17c2b351340f223312c75dc6fb73286e1a91c21ab83dac8ed986a8f41a3f0513caa664fa511186c5b1cb17992969fa55b995b7a7f5
|
7
|
+
data.tar.gz: 78d5aeb76e1d48a8ab1d1ad0b136c38a0c3da61fe1865a2ff8e6eb47d0751b4610609a1983a0fb0e431ef997f7a09eff5fd91bfdc9c33f6aac4ebf109d21be9b
|
@@ -30,27 +30,42 @@ module Rack
|
|
30
30
|
global_assign: GLOBAL_ASSIGN,
|
31
31
|
backup_suffix: BACKUP_SUFFIX
|
32
32
|
)
|
33
|
-
@index_html_path = index_html_path
|
33
|
+
@index_html_path = index_html_path.to_s
|
34
34
|
@global_assign = global_assign
|
35
|
-
@index_html_backup = index_html_path + backup_suffix
|
35
|
+
@index_html_backup = @index_html_path + backup_suffix
|
36
36
|
end
|
37
37
|
|
38
|
+
# Copy +index_html_path+ to index.html.original (see +BACKUP_SUFFIX+),
|
39
|
+
# and add the dynamic config to +index_html_path+.
|
40
|
+
# Use +as_string+ to avoid file writes.
|
38
41
|
def emplace(keys_and_values)
|
39
|
-
self.
|
40
|
-
json = JSON.generate(keys_and_values)
|
41
|
-
script = "#{@global_assign}=#{json}"
|
42
|
+
self.ensure_unmodified_html_backup
|
42
43
|
::File.open(@index_html_backup) do |f|
|
43
|
-
|
44
|
-
|
45
|
-
::File.write(@index_html_path, doc.serialize)
|
44
|
+
new_html = self.serialize(f, keys_and_values)
|
45
|
+
::File.write(@index_html_path, new_html)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
protected def
|
49
|
+
protected def ensure_unmodified_html_backup
|
50
50
|
return if ::File.exist?(@index_html_backup)
|
51
51
|
::FileUtils.move(@index_html_path, @index_html_backup)
|
52
52
|
end
|
53
53
|
|
54
|
+
# Return the new index.html with dynamic config as a string.
|
55
|
+
def as_string(keys_and_values)
|
56
|
+
::File.open(@index_html_path) do |f|
|
57
|
+
return self.serialize(f, keys_and_values)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
protected def serialize(f, keys_and_values)
|
62
|
+
json = JSON.generate(keys_and_values)
|
63
|
+
script = "#{@global_assign}=#{json}"
|
64
|
+
doc = Nokogiri::HTML5(f)
|
65
|
+
doc.at("head").prepend_child("<script>#{script}</script>")
|
66
|
+
return doc.serialize
|
67
|
+
end
|
68
|
+
|
54
69
|
def self.pick_env(regex_or_prefix)
|
55
70
|
return ENV.to_a.select { |(k, _v)| k.start_with?(regex_or_prefix) }.to_h if regex_or_prefix.is_a?(String)
|
56
71
|
return ENV.to_a.select { |(k, _v)| regex_or_prefix.match?(k) }.to_h
|
data/lib/rack/spa_app.rb
CHANGED
@@ -7,16 +7,31 @@ require "rack/spa_rewrite"
|
|
7
7
|
|
8
8
|
module Rack
|
9
9
|
class SpaApp
|
10
|
-
|
10
|
+
# Return Rack middleware dependencies for the SPA app.
|
11
|
+
# @param build_folder [String,Pathname] Path to the build folder.
|
12
|
+
# @param immutable [true,false] True to use +Rack::Immutable+ middleware,
|
13
|
+
# so that files that look fingerprinted (<name>.<git sha>.ext) get immutable cache headers.
|
14
|
+
# @param enforce_ssl [true,false] True to use +Rack::SsslEnforcer+. Requires `rack-ssl-enforcer` gem.
|
15
|
+
# @param service_worker_allowed [String,nil] Scope of the server worker for the Service-Worker-Allowed header.
|
16
|
+
# @param index_bytes [String,nil] Bytes of the app's index.html file. If not passed,
|
17
|
+
# use the contents of `<build_folder>/index.html`.
|
18
|
+
def self.dependencies(
|
19
|
+
build_folder,
|
20
|
+
immutable: true,
|
21
|
+
enforce_ssl: true,
|
22
|
+
service_worker_allowed: nil,
|
23
|
+
index_bytes: nil
|
24
|
+
)
|
25
|
+
index_bytes ||= File.read("#{build_folder}/index.html")
|
11
26
|
result = []
|
12
27
|
result << [Rack::SslEnforcer, {redirect_html: false}] if enforce_ssl
|
13
28
|
result << [Rack::ConditionalGet, {}]
|
14
29
|
result << [Rack::ETag, {}]
|
15
30
|
result << [Rack::Immutable, {match: immutable.is_a?(TrueClass) ? nil : immutable}] if immutable
|
16
|
-
result << [Rack::SpaRewrite, {
|
31
|
+
result << [Rack::SpaRewrite, {index_bytes:, html_only: true}]
|
17
32
|
result << [Rack::ServiceWorkerAllowed, {scope: service_worker_allowed}] if service_worker_allowed
|
18
33
|
result << [Rack::Static, {urls: [""], root: build_folder.to_s, cascade: true}]
|
19
|
-
result << [Rack::SpaRewrite, {
|
34
|
+
result << [Rack::SpaRewrite, {index_bytes:, html_only: false}]
|
20
35
|
return result
|
21
36
|
end
|
22
37
|
|
@@ -28,8 +43,8 @@ module Rack
|
|
28
43
|
builder.run Rack::LambdaApp.new(->(_) { raise "Should not see SpaApp fallback" })
|
29
44
|
end
|
30
45
|
|
31
|
-
def self.run_spa_app(builder, build_folder, enforce_ssl: true, immutable: true, **kw)
|
32
|
-
deps = self.dependencies(build_folder, enforce_ssl:, immutable:, **kw)
|
46
|
+
def self.run_spa_app(builder, build_folder, enforce_ssl: true, immutable: true, index_bytes: nil, **kw)
|
47
|
+
deps = self.dependencies(build_folder, enforce_ssl:, immutable:, index_bytes:, **kw)
|
33
48
|
self.install(builder, deps)
|
34
49
|
self.run(builder)
|
35
50
|
end
|
data/lib/rack/spa_rewrite.rb
CHANGED
@@ -3,21 +3,24 @@
|
|
3
3
|
require "rack"
|
4
4
|
|
5
5
|
module Rack
|
6
|
+
# Intercept HTTP requests and serve index.html.
|
7
|
+
# This middleware caches the index_html file (or the bytes can be passed in).
|
8
|
+
# If the file contents change, new content is NOT served.
|
6
9
|
class SpaRewrite
|
7
10
|
ALLOWED_VERBS = ["GET", "HEAD", "OPTIONS"].freeze
|
8
11
|
ALLOW_HEADER = ALLOWED_VERBS.join(", ")
|
9
12
|
|
10
|
-
|
13
|
+
# @param app The Rack app.
|
14
|
+
# @param index_bytes [String] The index.html contents to serve.
|
15
|
+
# @param html_only [true,false] True to only intercept html requests,
|
16
|
+
# false to intercept all requests. Usually you want to setup SpaRewrite
|
17
|
+
# first using html:false, then html:true as the final Rack app/middleware.
|
18
|
+
def initialize(app, index_bytes:, html_only:)
|
11
19
|
@app = app
|
12
|
-
@
|
20
|
+
@index_bytes = index_bytes
|
13
21
|
@html_only = html_only
|
14
|
-
begin
|
15
|
-
@index_mtime = ::File.mtime(@index_path).httpdate
|
16
|
-
rescue Errno::ENOENT
|
17
|
-
@index_mtime = Time.at(0)
|
18
|
-
end
|
19
|
-
@index_bytes = nil
|
20
22
|
@head = Rack::Head.new(->(env) { get env })
|
23
|
+
@started_at = Time.now.httpdate
|
21
24
|
end
|
22
25
|
|
23
26
|
def call(env)
|
@@ -37,15 +40,12 @@ module Rack
|
|
37
40
|
return [200, {"Allow" => ALLOW_HEADER, Rack::CONTENT_LENGTH => "0"}, []] if
|
38
41
|
request.options?
|
39
42
|
|
40
|
-
|
41
|
-
lastmodhttp = lastmod.httpdate
|
42
|
-
return [304, {}, []] if request.get_header("HTTP_IF_MODIFIED_SINCE") == lastmodhttp
|
43
|
+
return [304, {}, []] if request.get_header("HTTP_IF_MODIFIED_SINCE") == @started_at
|
43
44
|
|
44
|
-
@index_bytes = ::File.read(@index_path) if @index_bytes.nil? || @index_mtime < lastmodhttp
|
45
45
|
headers = {
|
46
46
|
"content-length" => @index_bytes.bytesize.to_s,
|
47
47
|
"content-type" => "text/html",
|
48
|
-
"last-modified" =>
|
48
|
+
"last-modified" => @started_at,
|
49
49
|
}
|
50
50
|
return [200, headers, [@index_bytes]]
|
51
51
|
end
|
data/lib/rack_spa.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-spa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lithic Tech
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|