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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b529c24ff76bbed44607ecbef4d5dd66414bded0cb8d1640cfb6945d1ae7e9c
4
- data.tar.gz: 2522b693b676608de3546183310970d7def3e05c6bbdf5561699fc434e4b3f6d
3
+ metadata.gz: '09c13a3ec4bb37595ede90e5536eca3159812d06c482397d971d1130eab3ceab'
4
+ data.tar.gz: 48cd4b6488ba57a185e510f00e6986b8514ad33887b7710431c40d9efcae4f45
5
5
  SHA512:
6
- metadata.gz: a628d623d424930d62deebff57273a72899b6399fc6ce2114410d0b1fa31293d427df9066238646d208c2878d20686a69bce6c80db5e548bee30e1b7c0ed45e3
7
- data.tar.gz: 53b3202f0f8858de9ae4ac123750ebe29c873c989f73a1e1a63defd96ebc20c2e7e935c3f88e2c00685fff7e4eec33190b892788331181bf5e2780869b1684dc
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!
@@ -18,6 +18,7 @@ module Bridgetown
18
18
  Bridgetown::Hooks.clear_reloadable_hooks
19
19
  site.plugin_manager.reload_plugin_files
20
20
  site.loaders_manager.reload_loaders
21
+ Bridgetown::Hooks.trigger :site, :post_reload, site
21
22
 
22
23
  ConsoleMethods.site_reset(site)
23
24
  end
@@ -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
- Bridgetown::Cache.clear_if_config_changed config unless soft
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(&block) # rubocop:disable Metrics/AbcSize
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
- hook = block&.(self) # provide additional setup hook
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.register :site, :post_write do
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}") }
@@ -5,6 +5,9 @@ services:
5
5
  buildCommand: bin/bridgetown deploy
6
6
  staticPublishPath: ./output
7
7
  pullRequestPreviewsEnabled: true
8
+ envVars:
9
+ - key: BRIDGETOWN_ENV
10
+ value: production
8
11
  headers:
9
12
  - path: /*
10
13
  name: X-Frame-Options
@@ -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
- # Returns an Array of unique keys for content for the Drop.
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 previous
48
- @previous ||= @obj.previous_resource.to_liquid
47
+ def next_resource
48
+ @next ||= @obj.next_resource.to_liquid
49
49
  end
50
+ alias_method :next, :next_resource
50
51
 
51
- def next
52
- @next ||= @obj.next_resource.to_liquid
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.register :generated_pages, :post_init, reloadable: false do |page|
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.register :resources, :post_read, reloadable: false do |resource|
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.register :site, :after_reset, reloadable: false do |_site|
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
- # register hook(s) to be called later
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
- # register a single hook to be called later
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
- def self.remove_hook(owner, _event, block)
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?(:url) ? args[0].relative_path : hook.block
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
- def self.clear_reloadable_hooks
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.exists?
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
- def _roda_run_main_route(r) # rubocop:disable Naming/MethodParameterName
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
- r.public
51
+ request.public
51
52
 
52
- r.root do
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.request
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(request)
55
- request.get "_bridgetown/live_reload" do
56
- {
57
- last_mod: File.stat(
58
- File.join(Bridgetown::Current.preloaded_configuration.destination, "index.html")
59
- ).mtime.to_i,
60
- }
61
- rescue StandardError => e
62
- { last_mod: 0, error: e.message }
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 first_mod = 0
405
- let connection_errors = 0
406
- const checkForReload = () => {
407
- fetch("/_bridgetown/live_reload").then(response => {
408
- if (response.ok) {
409
- response.json().then(data => {
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
- if (connection_errors < 20) setTimeout(() => checkForReload(), 6000)
420
- connection_errors++
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
- }).catch((err) => {
423
- if (connection_errors < 20) setTimeout(() => checkForReload(), 6000)
424
- connection_errors++
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
- checkForReload()
429
+ setTimeout(() => {
430
+ startReloadConnection()
431
+ }, 500)
428
432
  JAVASCRIPT
429
433
 
430
434
  %(<script type="module">#{code}</script>).html_safe
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bridgetown
4
- VERSION = "1.0.0.alpha10"
4
+ VERSION = "1.0.0.alpha11"
5
5
  CODE_NAME = "Pearl"
6
6
  end
@@ -4,71 +4,102 @@ module Bridgetown
4
4
  module Watcher
5
5
  extend self
6
6
 
7
- # Public: Continuously watch for file changes and rebuild the site
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 - The current site instance
11
- # options - A Hash containing the site configuration
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
- listener = build_listener(site, options)
18
- listener.start
14
+ listen(site, options)
19
15
 
20
- Bridgetown.logger.info "Auto-regeneration:", "enabled." unless options[:using_puma]
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
- # You pressed Ctrl-C, oh my!
26
+ # NOTE: not sure if this is needed any longer
33
27
  end
34
28
 
35
- private
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
- def build_listener(site, options)
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
- *paths_to_watch,
48
+ *load_paths_to_watch(site, options),
50
49
  ignore: listen_ignore_paths(options),
51
- force_polling: options["force_polling"],
52
- &listen_handler(site, options)
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 "Regenerating…"
64
- Bridgetown.logger.info "", "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}"
65
- c.each { |path| Bridgetown.logger.info "", path["#{site.root_dir}/".length..] }
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
- process(site, t, options)
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.alpha10
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-16 00:00:00.000000000 Z
11
+ date: 2021-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel