bridgetown-core 1.0.0.alpha10 → 1.0.0.alpha11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|