bridgetown-core 1.0.0.alpha10 → 1.0.0.alpha11
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/bridgetown-core/collection.rb +6 -6
- data/lib/bridgetown-core/commands/console.rb +1 -0
- data/lib/bridgetown-core/concerns/site/extensible.rb +8 -0
- data/lib/bridgetown-core/concerns/site/processable.rb +22 -4
- data/lib/bridgetown-core/concerns/site/ssr.rb +2 -17
- data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
- data/lib/bridgetown-core/configurations/render/render.yaml.erb +3 -0
- data/lib/bridgetown-core/drops/drop.rb +1 -1
- data/lib/bridgetown-core/drops/resource_drop.rb +28 -5
- data/lib/bridgetown-core/generators/prototype_generator.rb +3 -3
- data/lib/bridgetown-core/hooks.rb +51 -20
- data/lib/bridgetown-core/model/base.rb +24 -1
- data/lib/bridgetown-core/model/repo_origin.rb +48 -0
- data/lib/bridgetown-core/rack/roda.rb +4 -5
- data/lib/bridgetown-core/rack/routes.rb +44 -10
- data/lib/bridgetown-core/rack/static_indexes.rb +2 -0
- data/lib/bridgetown-core/resource/base.rb +2 -0
- data/lib/bridgetown-core/utils.rb +25 -21
- data/lib/bridgetown-core/version.rb +1 -1
- data/lib/bridgetown-core/watcher.rb +64 -60
- 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: '09c13a3ec4bb37595ede90e5536eca3159812d06c482397d971d1130eab3ceab'
|
4
|
+
data.tar.gz: 48cd4b6488ba57a185e510f00e6986b8514ad33887b7710431c40d9efcae4f45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '008f32b43e009a8349bc69fe01277c9a7ebdef2c7062d8c0dc331826d0eac394491a75b48892629d5a835eeef12e435c573d4c811ec33441555ec820870157d6'
|
7
|
+
data.tar.gz: b6cf7933d50093c1332b1b39f5764768a3d8cdc3ff91b115e3d31790afa350826815f30e7d3e8c3891ddffea236f40b8e5b52268ec8eb019b4e20f319f1171ef
|
@@ -297,12 +297,6 @@ module Bridgetown
|
|
297
297
|
resources << resource if site.config.unpublished || resource.published?
|
298
298
|
end
|
299
299
|
|
300
|
-
private
|
301
|
-
|
302
|
-
def container
|
303
|
-
@container ||= site.config["collections_dir"]
|
304
|
-
end
|
305
|
-
|
306
300
|
def sort_resources!
|
307
301
|
if metadata["sort_by"].is_a?(String)
|
308
302
|
sort_resources_by_key!
|
@@ -312,6 +306,12 @@ module Bridgetown
|
|
312
306
|
resources.reverse! if metadata.sort_direction == "descending"
|
313
307
|
end
|
314
308
|
|
309
|
+
private
|
310
|
+
|
311
|
+
def container
|
312
|
+
@container ||= site.config["collections_dir"]
|
313
|
+
end
|
314
|
+
|
315
315
|
# A custom sort function based on Schwartzian transform
|
316
316
|
# Refer https://byparker.com/blog/2017/schwartzian-transform-faster-sorting/ for details
|
317
317
|
def sort_resources_by_key!
|
@@ -56,5 +56,13 @@ class Bridgetown::Site
|
|
56
56
|
c.new(config)
|
57
57
|
end
|
58
58
|
end
|
59
|
+
|
60
|
+
# Shorthand for registering a site hook via {Bridgetown::Hooks}
|
61
|
+
# @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
|
62
|
+
# @yield the block will be called when the event is triggered
|
63
|
+
# @yieldparam site the site which triggered the event hook
|
64
|
+
def on(event, reloadable: false, &block)
|
65
|
+
Bridgetown::Hooks.register_one :site, event, reloadable: reloadable, &block
|
66
|
+
end
|
59
67
|
end
|
60
68
|
end
|
@@ -23,9 +23,9 @@ class Bridgetown::Site
|
|
23
23
|
|
24
24
|
# Reset all in-memory data and content.
|
25
25
|
#
|
26
|
-
|
26
|
+
# @param soft [Boolean] if true, persist some state and do a light refresh of layouts and data
|
27
27
|
# @return [void]
|
28
|
-
def reset(soft: false)
|
28
|
+
def reset(soft: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
29
29
|
self.time = Time.now
|
30
30
|
if config["time"]
|
31
31
|
self.time = Bridgetown::Utils.parse_date(
|
@@ -41,12 +41,30 @@ class Bridgetown::Site
|
|
41
41
|
@documents = nil
|
42
42
|
@docs_to_write = nil
|
43
43
|
@liquid_renderer.reset
|
44
|
-
frontmatter_defaults.reset unless soft
|
45
44
|
|
46
|
-
|
45
|
+
if soft
|
46
|
+
refresh_layouts_and_data
|
47
|
+
else
|
48
|
+
frontmatter_defaults.reset
|
49
|
+
Bridgetown::Cache.clear_if_config_changed config
|
50
|
+
end
|
51
|
+
|
47
52
|
Bridgetown::Hooks.trigger :site, (soft ? :after_soft_reset : :after_reset), self
|
48
53
|
end
|
49
54
|
|
55
|
+
# Read layouts and merge any new data collection contents into the site data
|
56
|
+
def refresh_layouts_and_data
|
57
|
+
reader.read_layouts
|
58
|
+
|
59
|
+
collections.data.tap do |coll|
|
60
|
+
coll.resources.clear
|
61
|
+
coll.read
|
62
|
+
coll.merge_data_resources.each do |k, v|
|
63
|
+
data[k] = v # refresh site data
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
50
68
|
# Read data from disk and load it into internal memory.
|
51
69
|
# @return [void]
|
52
70
|
def read
|
@@ -32,7 +32,7 @@ class Bridgetown::Site
|
|
32
32
|
@ssr_enabled = true
|
33
33
|
end
|
34
34
|
|
35
|
-
def ssr_setup
|
35
|
+
def ssr_setup
|
36
36
|
config.serving = true
|
37
37
|
Bridgetown::Hooks.trigger :site, :pre_read, self
|
38
38
|
defaults_reader.tap do |d|
|
@@ -46,27 +46,12 @@ class Bridgetown::Site
|
|
46
46
|
end
|
47
47
|
Bridgetown::Hooks.trigger :site, :post_read, self
|
48
48
|
|
49
|
-
|
49
|
+
yield self if block_given? # provide additional setup hook
|
50
50
|
return if Bridgetown.env.production?
|
51
51
|
|
52
|
-
@ssr_reload_hook = hook if hook.is_a?(Proc) && hook.lambda?
|
53
52
|
Bridgetown::Watcher.watch(self, config)
|
54
53
|
end
|
55
54
|
|
56
|
-
def ssr_reload
|
57
|
-
reset soft: true
|
58
|
-
reader.read_layouts
|
59
|
-
|
60
|
-
collections.data.tap do |coll|
|
61
|
-
coll.resources.clear
|
62
|
-
coll.read
|
63
|
-
coll.merge_data_resources.each do |k, v|
|
64
|
-
data[k] = v
|
65
|
-
end
|
66
|
-
end
|
67
|
-
@ssr_reload_hook.() if @ssr_reload_hook.is_a?(Proc)
|
68
|
-
end
|
69
|
-
|
70
55
|
def disable_ssr
|
71
56
|
Bridgetown.logger.info "SSR:", "now disabled."
|
72
57
|
@ssr_enabled = false
|
@@ -82,7 +82,7 @@ create_file "plugins/test_output.rb" do
|
|
82
82
|
<<~RUBY
|
83
83
|
module TestOutput
|
84
84
|
unless Bridgetown.env.development?
|
85
|
-
Bridgetown::Hooks.
|
85
|
+
Bridgetown::Hooks.register_one :site, :post_write do
|
86
86
|
# Load test suite to run on exit
|
87
87
|
require "nokogiri"
|
88
88
|
Dir["test/**/*.rb"].each { |file| require_relative("../\#{file}") }
|
@@ -110,7 +110,7 @@ module Bridgetown
|
|
110
110
|
# underlying data hashes and performs a set union to ensure a list
|
111
111
|
# of unique keys for the Drop.
|
112
112
|
#
|
113
|
-
#
|
113
|
+
# @return [Array<String>]
|
114
114
|
def keys
|
115
115
|
(content_methods |
|
116
116
|
mutations.keys |
|
@@ -6,7 +6,7 @@ module Bridgetown
|
|
6
6
|
extend Forwardable
|
7
7
|
|
8
8
|
NESTED_OBJECT_FIELD_BLACKLIST = %w(
|
9
|
-
content output excerpt next previous
|
9
|
+
content output excerpt next previous next_resource previous_resource
|
10
10
|
).freeze
|
11
11
|
|
12
12
|
mutable false
|
@@ -44,13 +44,15 @@ module Bridgetown
|
|
44
44
|
cmp
|
45
45
|
end
|
46
46
|
|
47
|
-
def
|
48
|
-
@
|
47
|
+
def next_resource
|
48
|
+
@next ||= @obj.next_resource.to_liquid
|
49
49
|
end
|
50
|
+
alias_method :next, :next_resource
|
50
51
|
|
51
|
-
def
|
52
|
-
@
|
52
|
+
def previous_resource
|
53
|
+
@previous ||= @obj.previous_resource.to_liquid
|
53
54
|
end
|
55
|
+
alias_method :previous, :previous_resource
|
54
56
|
|
55
57
|
# Generate a Hash for use in generating JSON.
|
56
58
|
# This is useful if fields need to be cleared before the JSON can generate.
|
@@ -79,6 +81,27 @@ module Bridgetown
|
|
79
81
|
result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
|
80
82
|
end
|
81
83
|
end
|
84
|
+
|
85
|
+
# Generates a list of keys with user content as their values.
|
86
|
+
# This gathers up the Drop methods and keys of the mutations and
|
87
|
+
# underlying data hashes and performs a set union to ensure a list
|
88
|
+
# of unique keys for the Drop.
|
89
|
+
#
|
90
|
+
# @return [Array<String>]
|
91
|
+
def keys
|
92
|
+
keys_to_remove = %w[next_resource previous_resource]
|
93
|
+
(content_methods |
|
94
|
+
mutations.keys |
|
95
|
+
fallback_data.keys).flatten.reject do |key|
|
96
|
+
keys_to_remove.include?(key)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Inspect the drop's keys and values through a JSON representation
|
101
|
+
# of its keys and values.
|
102
|
+
def inspect
|
103
|
+
JSON.pretty_generate hash_for_json
|
104
|
+
end
|
82
105
|
end
|
83
106
|
end
|
84
107
|
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Handles Generated Pages
|
4
|
-
Bridgetown::Hooks.
|
4
|
+
Bridgetown::Hooks.register_one :generated_pages, :post_init, reloadable: false do |page|
|
5
5
|
if page.class != Bridgetown::PrototypePage && page.data["prototype"].is_a?(Hash)
|
6
6
|
Bridgetown::PrototypeGenerator.add_matching_template(page)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
10
|
# Handles Resources
|
11
|
-
Bridgetown::Hooks.
|
11
|
+
Bridgetown::Hooks.register_one :resources, :post_read, reloadable: false do |resource|
|
12
12
|
if resource.data["prototype"].is_a?(Hash)
|
13
13
|
Bridgetown::PrototypeGenerator.add_matching_template(resource)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
# Ensure sites clear out templates before rebuild
|
18
|
-
Bridgetown::Hooks.
|
18
|
+
Bridgetown::Hooks.register_one :site, :after_reset, reloadable: false do |_site|
|
19
19
|
Bridgetown::PrototypeGenerator.matching_templates.clear
|
20
20
|
end
|
21
21
|
|
@@ -17,34 +17,59 @@ module Bridgetown
|
|
17
17
|
|
18
18
|
DEFAULT_PRIORITY = 20
|
19
19
|
|
20
|
-
# compatibility layer for octopress-hooks users
|
21
20
|
PRIORITY_MAP = {
|
22
21
|
low: 10,
|
23
22
|
normal: 20,
|
24
23
|
high: 30,
|
25
24
|
}.freeze
|
26
25
|
|
27
|
-
# initial empty hooks
|
28
26
|
@registry = {}
|
29
27
|
|
30
28
|
NotAvailable = Class.new(RuntimeError)
|
31
29
|
Uncallable = Class.new(RuntimeError)
|
32
30
|
|
33
|
-
# Ensure the priority is a Fixnum
|
34
31
|
def self.priority_value(priority)
|
35
32
|
return priority if priority.is_a?(Integer)
|
36
33
|
|
37
34
|
PRIORITY_MAP[priority] || DEFAULT_PRIORITY
|
38
35
|
end
|
39
36
|
|
40
|
-
#
|
37
|
+
# Sort registered hooks according to priority and load order
|
38
|
+
#
|
39
|
+
# @param hooks [Array<HookRegistration>]
|
40
|
+
def self.prioritized_hooks(hooks)
|
41
|
+
grouped_hooks = hooks.group_by(&:priority)
|
42
|
+
grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
|
43
|
+
end
|
44
|
+
|
45
|
+
# Register one or more hooks which may be triggered later for a particular event
|
46
|
+
#
|
47
|
+
# @param owners [Symbol, Array<Symbol>] name of the owner (`:site`, `:resource`, etc.)
|
48
|
+
# @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
|
49
|
+
# @param priority [Integer, Symbol] either `:low`, `:normal`, or `:high`, or an integer.
|
50
|
+
# Default is normal (20)
|
51
|
+
# @param reloadable [Boolean] whether the hook should be removed prior to a site reload.
|
52
|
+
# Default is true.
|
53
|
+
# @yield the block will be called when the event is triggered. Typically it receives at
|
54
|
+
# least one argument.
|
55
|
+
# @yieldparam obj the object which triggered the event hook
|
41
56
|
def self.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
|
42
57
|
Array(owners).each do |owner|
|
43
58
|
register_one(owner, event, priority: priority, reloadable: reloadable, &block)
|
44
59
|
end
|
45
60
|
end
|
46
61
|
|
47
|
-
#
|
62
|
+
# Register a hook which may be triggered later for a particular event
|
63
|
+
#
|
64
|
+
# @param owner [Symbol] name of the owner (`:site`, `:resource`, etc.)
|
65
|
+
# @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
|
66
|
+
# @param priority [Integer, Symbol] either `:low`, `:normal`, or `:high`, or an integer.
|
67
|
+
# Default is normal (20)
|
68
|
+
# @param reloadable [Boolean] whether the hook should be removed prior to a site reload.
|
69
|
+
# Default is true.
|
70
|
+
# @yield the block will be called when the event is triggered. Typically it receives at
|
71
|
+
# least one argument.
|
72
|
+
# @yieldparam obj the object which triggered the event hook
|
48
73
|
def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
|
49
74
|
@registry[owner] ||= []
|
50
75
|
|
@@ -68,10 +93,28 @@ module Bridgetown
|
|
68
93
|
block
|
69
94
|
end
|
70
95
|
|
71
|
-
|
96
|
+
# Delete a previously-registered hook
|
97
|
+
#
|
98
|
+
# @param owners [Symbol] name of the owner (`:site`, `:resource`, etc.)
|
99
|
+
# @param block [Proc] the exact block used originally to register the hook
|
100
|
+
def self.remove_hook(owner, block)
|
72
101
|
@registry[owner].delete_if { |item| item.block == block }
|
73
102
|
end
|
74
103
|
|
104
|
+
# Clear all hooks marked as reloadable from the registry
|
105
|
+
def self.clear_reloadable_hooks
|
106
|
+
Bridgetown.logger.debug("Clearing reloadable hooks")
|
107
|
+
|
108
|
+
@registry.each_value do |hooks|
|
109
|
+
hooks.delete_if(&:reloadable)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Trigger all registered hooks for a particular owner and event.
|
114
|
+
# Any arguments after the initial two will be directly passed along to the hooks.
|
115
|
+
#
|
116
|
+
# @param owner [Symbol] name of the owner (`:site`, `:resource`, etc.)
|
117
|
+
# @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
|
75
118
|
def self.trigger(owner, event, *args) # rubocop:disable Metrics/CyclomaticComplexity
|
76
119
|
# proceed only if there are hooks to call
|
77
120
|
hooks = @registry[owner]&.select { |item| item.event == event }
|
@@ -79,25 +122,13 @@ module Bridgetown
|
|
79
122
|
|
80
123
|
prioritized_hooks(hooks).each do |hook|
|
81
124
|
if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
|
82
|
-
hook_info = args[0].respond_to?(:
|
125
|
+
hook_info = args[0].respond_to?(:relative_path) ? args[0].relative_path : hook.block
|
83
126
|
Bridgetown.logger.debug("Triggering hook:", "#{owner}:#{event} for #{hook_info}")
|
84
127
|
end
|
85
128
|
hook.block.call(*args)
|
86
129
|
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.prioritized_hooks(hooks)
|
90
|
-
# sort hooks according to priority and load order
|
91
|
-
grouped_hooks = hooks.group_by(&:priority)
|
92
|
-
grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
|
93
|
-
end
|
94
130
|
|
95
|
-
|
96
|
-
Bridgetown.logger.debug("Clearing reloadable hooks")
|
97
|
-
|
98
|
-
@registry.each_value do |hooks|
|
99
|
-
hooks.delete_if(&:reloadable)
|
100
|
-
end
|
131
|
+
true
|
101
132
|
end
|
102
133
|
end
|
103
134
|
end
|
@@ -69,8 +69,23 @@ module Bridgetown
|
|
69
69
|
attributes[:_origin_]
|
70
70
|
end
|
71
71
|
|
72
|
+
def origin=(new_origin)
|
73
|
+
attributes[:_id_] = new_origin.id
|
74
|
+
attributes[:_origin_] = new_origin
|
75
|
+
end
|
76
|
+
|
72
77
|
def persisted?
|
73
|
-
id && origin
|
78
|
+
(id && origin&.exists?) == true
|
79
|
+
end
|
80
|
+
|
81
|
+
def save
|
82
|
+
unless origin.respond_to?(:write)
|
83
|
+
raise "`#{origin.class}' doesn't allow writing of model objects"
|
84
|
+
end
|
85
|
+
|
86
|
+
run_callbacks :save do
|
87
|
+
origin.write(self)
|
88
|
+
end
|
74
89
|
end
|
75
90
|
|
76
91
|
# @return [Bridgetown::Resource::Base]
|
@@ -100,11 +115,19 @@ module Bridgetown
|
|
100
115
|
attributes[:_collection_]
|
101
116
|
end
|
102
117
|
|
118
|
+
def collection=(new_collection)
|
119
|
+
attributes[:_collection_] = new_collection
|
120
|
+
end
|
121
|
+
|
103
122
|
# @return [String]
|
104
123
|
def content
|
105
124
|
attributes[:_content_]
|
106
125
|
end
|
107
126
|
|
127
|
+
def content=(new_content)
|
128
|
+
attributes[:_content_] = new_content
|
129
|
+
end
|
130
|
+
|
108
131
|
def attributes
|
109
132
|
@attributes ||= HashWithDotAccess::Hash.new
|
110
133
|
end
|
@@ -25,6 +25,18 @@ module Bridgetown
|
|
25
25
|
def data_file_extensions
|
26
26
|
%w(.yaml .yml .json .csv .tsv .rb).freeze
|
27
27
|
end
|
28
|
+
|
29
|
+
# Initializes a new repo object using a collection and a relative source path.
|
30
|
+
# You'll need to use this when you want to create and save a model to the source.
|
31
|
+
#
|
32
|
+
# @param collection [Bridgetown::Collection, String, Symbol] either a collection
|
33
|
+
# label or Collection object
|
34
|
+
# @param relative_path [Pathname, String] the source path of the file to save
|
35
|
+
def new_with_collection_path(collection, relative_path)
|
36
|
+
collection = collection.label if collection.is_a?(Bridgetown::Collection)
|
37
|
+
|
38
|
+
new("repo://#{collection}.collection/#{relative_path}")
|
39
|
+
end
|
28
40
|
end
|
29
41
|
|
30
42
|
def read
|
@@ -46,6 +58,23 @@ module Bridgetown
|
|
46
58
|
@data
|
47
59
|
end
|
48
60
|
|
61
|
+
def write(model)
|
62
|
+
if File.exist?(original_path) && !Bridgetown::Utils.has_yaml_header?(original_path)
|
63
|
+
raise Bridgetown::Errors::InvalidYAMLFrontMatterError,
|
64
|
+
"Only existing files containing YAML front matter can be overwritten by the model"
|
65
|
+
end
|
66
|
+
|
67
|
+
contents = "#{front_matter_to_yaml(model)}---\n\n#{model.content}"
|
68
|
+
|
69
|
+
# Create folders if necessary
|
70
|
+
dir = File.dirname(original_path)
|
71
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
72
|
+
|
73
|
+
File.write(original_path, contents)
|
74
|
+
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
49
78
|
def url
|
50
79
|
@url ||= URI.parse(id)
|
51
80
|
end
|
@@ -121,6 +150,25 @@ module Bridgetown
|
|
121
150
|
raise error
|
122
151
|
end
|
123
152
|
end
|
153
|
+
|
154
|
+
def front_matter_to_yaml(model)
|
155
|
+
data = model.data_attributes.to_h
|
156
|
+
data = data.deep_merge(data) do |_, _, v|
|
157
|
+
case v
|
158
|
+
when DateTime
|
159
|
+
v.to_time
|
160
|
+
when Symbol
|
161
|
+
v.to_s
|
162
|
+
else
|
163
|
+
v
|
164
|
+
end
|
165
|
+
end
|
166
|
+
data.each do |k, v|
|
167
|
+
data.delete(k) if v.nil?
|
168
|
+
end
|
169
|
+
|
170
|
+
data.to_yaml
|
171
|
+
end
|
124
172
|
end
|
125
173
|
end
|
126
174
|
end
|
@@ -23,6 +23,7 @@ module Bridgetown
|
|
23
23
|
plugin :json
|
24
24
|
plugin :json_parser
|
25
25
|
plugin :cookies
|
26
|
+
plugin :streaming
|
26
27
|
plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
|
27
28
|
plugin :not_found do
|
28
29
|
output_folder = Bridgetown::Current.preloaded_configuration.destination
|
@@ -39,7 +40,7 @@ module Bridgetown
|
|
39
40
|
"500 Internal Server Error"
|
40
41
|
end
|
41
42
|
|
42
|
-
|
43
|
+
before do
|
43
44
|
if self.class.opts[:bridgetown_site]
|
44
45
|
# The site had previously been initialized via the bridgetown_ssr plugin
|
45
46
|
Bridgetown::Current.site ||= self.class.opts[:bridgetown_site]
|
@@ -47,14 +48,12 @@ module Bridgetown
|
|
47
48
|
Bridgetown::Current.preloaded_configuration ||=
|
48
49
|
self.class.opts[:bridgetown_preloaded_config]
|
49
50
|
|
50
|
-
|
51
|
+
request.public
|
51
52
|
|
52
|
-
|
53
|
+
request.root do
|
53
54
|
output_folder = Bridgetown::Current.preloaded_configuration.destination
|
54
55
|
File.read(File.join(output_folder, "index.html"))
|
55
56
|
end
|
56
|
-
|
57
|
-
super
|
58
57
|
end
|
59
58
|
|
60
59
|
# Helper shorthand for Bridgetown::Current.site
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
module Bridgetown
|
4
4
|
module Rack
|
5
|
+
@interrupted = false
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :interrupted
|
9
|
+
end
|
10
|
+
|
5
11
|
class Routes
|
6
12
|
class << self
|
7
13
|
attr_accessor :tracked_subclasses, :router_block
|
@@ -37,7 +43,7 @@ module Bridgetown
|
|
37
43
|
def start!(roda_app)
|
38
44
|
if Bridgetown.env.development? &&
|
39
45
|
!Bridgetown::Current.preloaded_configuration.skip_live_reload
|
40
|
-
setup_live_reload roda_app
|
46
|
+
setup_live_reload roda_app
|
41
47
|
end
|
42
48
|
|
43
49
|
Bridgetown::Rack::Routes.tracked_subclasses&.each_value do |klass|
|
@@ -51,15 +57,31 @@ module Bridgetown
|
|
51
57
|
nil
|
52
58
|
end
|
53
59
|
|
54
|
-
def setup_live_reload(
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
def setup_live_reload(app) # rubocop:disable Metrics/AbcSize
|
61
|
+
sleep_interval = 0.2
|
62
|
+
file_to_check = File.join(app.class.opts[:bridgetown_preloaded_config].destination,
|
63
|
+
"index.html")
|
64
|
+
|
65
|
+
app.request.get "_bridgetown/live_reload" do
|
66
|
+
app.response["Content-Type"] = "text/event-stream"
|
67
|
+
|
68
|
+
@_mod = File.exist?(file_to_check) ? File.stat(file_to_check).mtime.to_i : 0
|
69
|
+
app.stream async: true do |out|
|
70
|
+
# 5 second intervals so Puma's threads aren't all exausted
|
71
|
+
(5 / sleep_interval).to_i.times do
|
72
|
+
break if Bridgetown::Rack.interrupted
|
73
|
+
|
74
|
+
new_mod = File.exist?(file_to_check) ? File.stat(file_to_check).mtime.to_i : 0
|
75
|
+
if @_mod < new_mod
|
76
|
+
out << "data: reloaded!\n\n"
|
77
|
+
break
|
78
|
+
else
|
79
|
+
out << "data: #{new_mod}\n\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
sleep sleep_interval
|
83
|
+
end
|
84
|
+
end
|
63
85
|
end
|
64
86
|
end
|
65
87
|
end
|
@@ -86,3 +108,15 @@ module Bridgetown
|
|
86
108
|
end
|
87
109
|
end
|
88
110
|
end
|
111
|
+
|
112
|
+
if Bridgetown.env.development? &&
|
113
|
+
!Bridgetown::Current.preloaded_configuration.skip_live_reload
|
114
|
+
Puma::Launcher.class_eval do
|
115
|
+
alias_method :_old_stop, :stop
|
116
|
+
def stop
|
117
|
+
Bridgetown::Rack.interrupted = true
|
118
|
+
|
119
|
+
_old_stop
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "roda/plugins/public"
|
4
4
|
|
5
|
+
# NOTE: once this upstreamed PR is merged in we can remove this file.
|
6
|
+
# https://github.com/jeremyevans/roda/pull/215
|
5
7
|
Roda::RodaPlugins::Public::RequestMethods.module_eval do
|
6
8
|
SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact) # rubocop:disable Lint/ConstantDefinitionInBlock
|
7
9
|
def public_path_segments(path) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
@@ -284,12 +284,14 @@ module Bridgetown
|
|
284
284
|
collection.resources[pos + 1] if pos && pos < collection.resources.length - 1
|
285
285
|
end
|
286
286
|
alias_method :next_doc, :next_resource
|
287
|
+
alias_method :next, :next_resource
|
287
288
|
|
288
289
|
def previous_resource
|
289
290
|
pos = collection.resources.index { |item| item.equal?(self) }
|
290
291
|
collection.resources[pos - 1] if pos&.positive?
|
291
292
|
end
|
292
293
|
alias_method :previous_doc, :previous_resource
|
294
|
+
alias_method :previous, :previous_resource
|
293
295
|
|
294
296
|
private
|
295
297
|
|
@@ -401,30 +401,34 @@ module Bridgetown
|
|
401
401
|
return "" unless Bridgetown.env.development? && !site.config.skip_live_reload
|
402
402
|
|
403
403
|
code = <<~JAVASCRIPT
|
404
|
-
let
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
if (
|
409
|
-
|
410
|
-
const last_mod = data.last_mod
|
411
|
-
if (first_mod === 0) {
|
412
|
-
first_mod = last_mod
|
413
|
-
} else if (last_mod > first_mod) {
|
414
|
-
location.reload()
|
415
|
-
}
|
416
|
-
setTimeout(() => checkForReload(), 700)
|
417
|
-
})
|
404
|
+
let lastmod = 0
|
405
|
+
function startReloadConnection() {
|
406
|
+
const evtSource = new EventSource("/_bridgetown/live_reload")
|
407
|
+
evtSource.onmessage = event => {
|
408
|
+
if (event.data == "reloaded!") {
|
409
|
+
location.reload()
|
418
410
|
} else {
|
419
|
-
|
420
|
-
|
411
|
+
const newmod = Number(event.data)
|
412
|
+
if (lastmod > 0 && newmod > 0 && lastmod < newmod) {
|
413
|
+
location.reload()
|
414
|
+
} else {
|
415
|
+
lastmod = newmod
|
416
|
+
}
|
421
417
|
}
|
422
|
-
}
|
423
|
-
|
424
|
-
|
425
|
-
|
418
|
+
}
|
419
|
+
evtSource.onerror = event => {
|
420
|
+
if (evtSource.readyState === 2) {
|
421
|
+
// reconnect with new object
|
422
|
+
evtSource.close()
|
423
|
+
console.warn("Live reload: attempting to reconnect in 3 seconds...")
|
424
|
+
|
425
|
+
setTimeout(() => startReloadConnection(), 3000)
|
426
|
+
}
|
427
|
+
}
|
426
428
|
}
|
427
|
-
|
429
|
+
setTimeout(() => {
|
430
|
+
startReloadConnection()
|
431
|
+
}, 500)
|
428
432
|
JAVASCRIPT
|
429
433
|
|
430
434
|
%(<script type="module">#{code}</script>).html_safe
|
@@ -4,71 +4,102 @@ module Bridgetown
|
|
4
4
|
module Watcher
|
5
5
|
extend self
|
6
6
|
|
7
|
-
#
|
8
|
-
# whenever a change is detected.
|
7
|
+
# Continuously watch for file changes and rebuild the site whenever a change is detected.
|
9
8
|
#
|
10
|
-
# site
|
11
|
-
# options
|
12
|
-
#
|
13
|
-
# Returns nothing.
|
9
|
+
# @param site [Bridgetown::Site] the current site instance
|
10
|
+
# @param options [Bridgetown::Configuration] the site configuration
|
14
11
|
def watch(site, options)
|
15
12
|
ENV["LISTEN_GEM_DEBUGGING"] ||= "1" if options["verbose"]
|
16
13
|
|
17
|
-
|
18
|
-
listener.start
|
14
|
+
listen(site, options)
|
19
15
|
|
20
|
-
Bridgetown.logger.info "
|
16
|
+
Bridgetown.logger.info "Watcher:", "enabled." unless options[:using_puma]
|
21
17
|
|
22
18
|
unless options[:serving]
|
23
19
|
trap("INT") do
|
24
|
-
listener.stop
|
25
|
-
Bridgetown.logger.info "", "Halting auto-regeneration."
|
26
20
|
exit 0
|
27
21
|
end
|
28
22
|
|
29
23
|
sleep_forever
|
30
24
|
end
|
31
25
|
rescue ThreadError
|
32
|
-
#
|
26
|
+
# NOTE: not sure if this is needed any longer
|
33
27
|
end
|
34
28
|
|
35
|
-
|
29
|
+
# Return a list of load paths which should be watched for changes
|
30
|
+
#
|
31
|
+
# @param (see #watch)
|
32
|
+
def load_paths_to_watch(site, options)
|
33
|
+
site.plugin_manager.plugins_path.select { |path| Dir.exist?(path) }
|
34
|
+
.then do |paths|
|
35
|
+
(paths + options.autoload_paths).uniq
|
36
|
+
end
|
37
|
+
end
|
36
38
|
|
37
|
-
|
39
|
+
# Start a listener to watch for changes and call {#reload_site}
|
40
|
+
#
|
41
|
+
# @param (see #watch)
|
42
|
+
def listen(site, options)
|
38
43
|
webpack_path = site.in_root_dir(".bridgetown-webpack")
|
39
44
|
FileUtils.mkdir(webpack_path) unless Dir.exist?(webpack_path)
|
40
|
-
plugin_paths_to_watch = site.plugin_manager.plugins_path.select do |path|
|
41
|
-
Dir.exist?(path)
|
42
|
-
end
|
43
|
-
|
44
|
-
paths_to_watch = (plugin_paths_to_watch + options.autoload_paths).uniq
|
45
|
-
|
46
45
|
Listen.to(
|
47
46
|
options["source"],
|
48
47
|
webpack_path,
|
49
|
-
*
|
48
|
+
*load_paths_to_watch(site, options),
|
50
49
|
ignore: listen_ignore_paths(options),
|
51
|
-
force_polling: options["force_polling"]
|
52
|
-
|
53
|
-
)
|
54
|
-
end
|
55
|
-
|
56
|
-
def listen_handler(site, options)
|
57
|
-
proc do |modified, added, removed|
|
58
|
-
t = Time.now
|
50
|
+
force_polling: options["force_polling"]
|
51
|
+
) do |modified, added, removed|
|
59
52
|
c = modified + added + removed
|
60
53
|
n = c.length
|
61
54
|
|
62
55
|
unless site.ssr?
|
63
|
-
Bridgetown.logger.info
|
64
|
-
|
65
|
-
|
56
|
+
Bridgetown.logger.info(
|
57
|
+
"Reloading…",
|
58
|
+
"#{n} file#{"s" if c.length > 1} changed at #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
|
59
|
+
)
|
60
|
+
c.each { |path| Bridgetown.logger.info "", "- #{path["#{site.root_dir}/".length..]}" }
|
66
61
|
end
|
67
62
|
|
68
|
-
|
63
|
+
reload_site(site, options)
|
64
|
+
end.start
|
65
|
+
end
|
66
|
+
|
67
|
+
# Reload the site including plugins and Zeitwerk autoloaders and process it (unless SSR)
|
68
|
+
#
|
69
|
+
# @param (see #watch)
|
70
|
+
def reload_site(site, options) # rubocop:todo Metrics/MethodLength
|
71
|
+
begin
|
72
|
+
time = Time.now
|
73
|
+
I18n.reload! # make sure any locale files get read again
|
74
|
+
Bridgetown::Current.site = site # needed in SSR mode apparently
|
75
|
+
Bridgetown::Hooks.trigger :site, :pre_reload, site
|
76
|
+
Bridgetown::Hooks.clear_reloadable_hooks
|
77
|
+
site.plugin_manager.reload_plugin_files
|
78
|
+
site.loaders_manager.reload_loaders
|
79
|
+
Bridgetown::Hooks.trigger :site, :post_reload, site
|
80
|
+
|
81
|
+
if site.ssr?
|
82
|
+
site.reset(soft: true)
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
site.process
|
87
|
+
Bridgetown.logger.info "Done! 🎉", "#{"Completed".green} in less than" \
|
88
|
+
" #{(Time.now - time).ceil(2)} seconds."
|
89
|
+
rescue Exception => e
|
90
|
+
Bridgetown.logger.error "Error:", e.message
|
91
|
+
|
92
|
+
if options[:trace]
|
93
|
+
Bridgetown.logger.info e.backtrace.join("\n")
|
94
|
+
else
|
95
|
+
Bridgetown.logger.warn "Backtrace:", "Use the --trace option for more information."
|
96
|
+
end
|
69
97
|
end
|
98
|
+
Bridgetown.logger.info ""
|
70
99
|
end
|
71
100
|
|
101
|
+
private
|
102
|
+
|
72
103
|
def normalize_encoding(obj, desired_encoding)
|
73
104
|
case obj
|
74
105
|
when Array
|
@@ -126,32 +157,5 @@ module Bridgetown
|
|
126
157
|
def sleep_forever
|
127
158
|
loop { sleep 1000 }
|
128
159
|
end
|
129
|
-
|
130
|
-
# @param site [Bridgetown::Site]
|
131
|
-
def process(site, time, options)
|
132
|
-
begin
|
133
|
-
I18n.reload! # make sure any locale files get read again
|
134
|
-
Bridgetown::Current.site = site # needed in SSR mode apparently
|
135
|
-
Bridgetown::Hooks.trigger :site, :pre_reload, site
|
136
|
-
Bridgetown::Hooks.clear_reloadable_hooks
|
137
|
-
site.plugin_manager.reload_plugin_files
|
138
|
-
site.loaders_manager.reload_loaders
|
139
|
-
|
140
|
-
return site.ssr_reload if site.ssr?
|
141
|
-
|
142
|
-
site.process
|
143
|
-
Bridgetown.logger.info "Done! 🎉", "#{"Completed".green} in less than" \
|
144
|
-
" #{(Time.now - time).ceil(2)} seconds."
|
145
|
-
rescue Exception => e
|
146
|
-
Bridgetown.logger.error "Error:", e.message
|
147
|
-
|
148
|
-
if options[:trace]
|
149
|
-
Bridgetown.logger.info e.backtrace.join("\n")
|
150
|
-
else
|
151
|
-
Bridgetown.logger.warn "Backtrace:", "Use the --trace option for more information."
|
152
|
-
end
|
153
|
-
end
|
154
|
-
Bridgetown.logger.info ""
|
155
|
-
end
|
156
160
|
end
|
157
161
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bridgetown-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.alpha11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bridgetown Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|