bridgetown-core 1.0.0.alpha9 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/bin/bridgetown +6 -1
  4. data/bridgetown-core.gemspec +4 -3
  5. data/lib/bridgetown-core/collection.rb +6 -6
  6. data/lib/bridgetown-core/commands/base.rb +18 -18
  7. data/lib/bridgetown-core/commands/build.rb +1 -1
  8. data/lib/bridgetown-core/commands/clean.rb +2 -2
  9. data/lib/bridgetown-core/commands/console.rb +1 -0
  10. data/lib/bridgetown-core/commands/esbuild/esbuild.config.js +27 -0
  11. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +216 -0
  12. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +47 -0
  13. data/lib/bridgetown-core/commands/esbuild/setup.rb +4 -0
  14. data/lib/bridgetown-core/commands/esbuild/update.rb +4 -0
  15. data/lib/bridgetown-core/commands/esbuild.rb +83 -0
  16. data/lib/bridgetown-core/commands/new.rb +80 -10
  17. data/lib/bridgetown-core/commands/plugins.rb +1 -1
  18. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +1 -1
  19. data/lib/bridgetown-core/commands/webpack/update.rb +3 -3
  20. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +1 -1
  21. data/lib/bridgetown-core/commands/webpack.rb +3 -3
  22. data/lib/bridgetown-core/component.rb +9 -3
  23. data/lib/bridgetown-core/concerns/site/configurable.rb +6 -0
  24. data/lib/bridgetown-core/concerns/site/content.rb +4 -4
  25. data/lib/bridgetown-core/concerns/site/extensible.rb +8 -0
  26. data/lib/bridgetown-core/concerns/site/processable.rb +23 -4
  27. data/lib/bridgetown-core/concerns/site/ssr.rb +2 -17
  28. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  29. data/lib/bridgetown-core/configurations/purgecss.rb +2 -1
  30. data/lib/bridgetown-core/configurations/render/render.yaml.erb +3 -0
  31. data/lib/bridgetown-core/configurations/stimulus.rb +41 -12
  32. data/lib/bridgetown-core/configurations/tailwindcss/css_imports.css +5 -0
  33. data/lib/bridgetown-core/configurations/tailwindcss.rb +31 -2
  34. data/lib/bridgetown-core/configurations/turbo/turbo_transitions.js +48 -0
  35. data/lib/bridgetown-core/configurations/turbo.rb +15 -5
  36. data/lib/bridgetown-core/configurations/vercel/vercel.json +45 -0
  37. data/lib/bridgetown-core/configurations/vercel/vercel_url.rb +12 -0
  38. data/lib/bridgetown-core/configurations/vercel.rb +4 -0
  39. data/lib/bridgetown-core/converters/erb_templates.rb +7 -9
  40. data/lib/bridgetown-core/converters/ruby_templates.rb +1 -1
  41. data/lib/bridgetown-core/converters/serbea_templates.rb +5 -8
  42. data/lib/bridgetown-core/drops/drop.rb +1 -1
  43. data/lib/bridgetown-core/drops/resource_drop.rb +28 -5
  44. data/lib/bridgetown-core/errors.rb +21 -0
  45. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -3
  46. data/lib/bridgetown-core/helpers.rb +3 -2
  47. data/lib/bridgetown-core/hooks.rb +51 -20
  48. data/lib/bridgetown-core/model/base.rb +24 -1
  49. data/lib/bridgetown-core/model/origin.rb +4 -6
  50. data/lib/bridgetown-core/model/repo_origin.rb +48 -0
  51. data/lib/bridgetown-core/rack/boot.rb +5 -9
  52. data/lib/bridgetown-core/rack/roda.rb +4 -5
  53. data/lib/bridgetown-core/rack/routes.rb +44 -10
  54. data/lib/bridgetown-core/rack/static_indexes.rb +2 -0
  55. data/lib/bridgetown-core/resource/base.rb +3 -1
  56. data/lib/bridgetown-core/ruby_template_view.rb +11 -0
  57. data/lib/bridgetown-core/site.rb +5 -0
  58. data/lib/bridgetown-core/tags/{webpack_path.rb → asset_path.rb} +7 -9
  59. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -1
  60. data/lib/bridgetown-core/utils/loaders_manager.rb +17 -0
  61. data/lib/bridgetown-core/utils.rb +88 -30
  62. data/lib/bridgetown-core/version.rb +1 -1
  63. data/lib/bridgetown-core/watcher.rb +74 -70
  64. data/lib/bridgetown-core.rb +1 -0
  65. data/lib/site_template/Gemfile.erb +17 -11
  66. data/lib/site_template/README.md +2 -2
  67. data/lib/site_template/{Rakefile → Rakefile.erb} +15 -0
  68. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.erb +11 -0
  69. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.rb +5 -0
  70. data/lib/site_template/TEMPLATES/erb/_layouts/default.erb +15 -0
  71. data/lib/site_template/TEMPLATES/erb/_layouts/page.erb +7 -0
  72. data/lib/site_template/TEMPLATES/erb/_layouts/post.erb +7 -0
  73. data/lib/site_template/TEMPLATES/erb/_partials/_footer.erb +3 -0
  74. data/lib/site_template/TEMPLATES/erb/_partials/_head.erb +10 -0
  75. data/lib/site_template/{src → TEMPLATES/liquid}/_components/footer.liquid +0 -0
  76. data/lib/site_template/TEMPLATES/liquid/_components/head.liquid +10 -0
  77. data/lib/site_template/TEMPLATES/liquid/_components/navbar.liquid +11 -0
  78. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/default.liquid +2 -2
  79. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/page.liquid +0 -0
  80. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/post.liquid +0 -0
  81. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.rb +5 -0
  82. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.serb +11 -0
  83. data/lib/site_template/TEMPLATES/serbea/_layouts/default.serb +15 -0
  84. data/lib/site_template/TEMPLATES/serbea/_layouts/page.serb +7 -0
  85. data/lib/site_template/TEMPLATES/serbea/_layouts/post.serb +7 -0
  86. data/lib/site_template/TEMPLATES/serbea/_partials/_footer.serb +3 -0
  87. data/lib/site_template/TEMPLATES/serbea/_partials/_head.serb +10 -0
  88. data/lib/site_template/frontend/javascript/index.js.erb +7 -3
  89. data/lib/site_template/frontend/styles/index.css +71 -6
  90. data/lib/site_template/package.json.erb +24 -9
  91. data/lib/site_template/ruby-version.erb +1 -0
  92. data/lib/site_template/server/roda_app.rb +5 -3
  93. data/lib/site_template/server/routes/hello.rb.sample +1 -1
  94. data/lib/site_template/src/images/logo.svg +91 -0
  95. data/lib/site_template/src/index.md.erb +22 -0
  96. data/lib/site_template/src/posts.md.erb +28 -0
  97. metadata +70 -22
  98. data/lib/bridgetown-core/configurations/swup.rb +0 -37
  99. data/lib/site_template/frontend/styles/index.scss +0 -17
  100. data/lib/site_template/src/_components/head.liquid +0 -10
  101. data/lib/site_template/src/_components/navbar.liquid +0 -5
  102. data/lib/site_template/src/_layouts/home.liquid +0 -7
  103. data/lib/site_template/src/images/.keep +0 -1
  104. data/lib/site_template/src/index.md +0 -7
  105. data/lib/site_template/src/posts.md +0 -14
@@ -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
@@ -30,7 +30,7 @@ module Bridgetown
30
30
  def initialize(model:)
31
31
  @model = model
32
32
  @site = model.site
33
- @data = front_matter_defaults
33
+ @data = collection.data? ? HashWithDotAccess::Hash.new : front_matter_defaults
34
34
 
35
35
  trigger_hooks :post_init
36
36
  end
@@ -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
 
@@ -73,6 +73,10 @@ module Bridgetown
73
73
  helpers.respond_to?(method_name.to_sym, include_private) || super
74
74
  end
75
75
 
76
+ def inspect
77
+ "#<#{self.class} layout=#{layout&.label} resource=#{resource.relative_path}>"
78
+ end
79
+
76
80
  private
77
81
 
78
82
  def _render_statement(component, options)
@@ -103,5 +107,12 @@ module Bridgetown
103
107
  strict_variables: site.config["liquid"]["strict_variables"],
104
108
  }
105
109
  end
110
+
111
+ def _partial_path(partial_name, ext)
112
+ partial_name = partial_name.split("/").tap { _1.last.prepend("_") }.join("/")
113
+
114
+ # TODO: see if there's a workaround for this to speed up performance
115
+ site.in_source_dir(site.config[:partials_dir], "#{partial_name}.#{ext}")
116
+ end
106
117
  end
107
118
  end
@@ -53,6 +53,7 @@ module Bridgetown
53
53
  @reader = Reader.new(self)
54
54
  @liquid_renderer = LiquidRenderer.new(self)
55
55
 
56
+ Bridgetown::Cache.base_cache["site_tmp"] = {}.with_dot_access
56
57
  ensure_not_in_dest
57
58
 
58
59
  Bridgetown::Current.site = self
@@ -74,6 +75,10 @@ module Bridgetown
74
75
  end
75
76
  end
76
77
 
78
+ def tmp_cache
79
+ Bridgetown::Cache.base_cache["site_tmp"]
80
+ end
81
+
77
82
  def inspect
78
83
  "#<Bridgetown::Site #{metadata.inspect.delete_prefix("{").delete_suffix("}")}>"
79
84
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Bridgetown
4
4
  module Tags
5
- # A helper class to help find the path to webpack asset inside of a webpack
5
+ # A helper class to help find the path to assets inside of a webpack or esbuild
6
6
  # manifest file.
7
- class WebpackPath < Liquid::Tag
7
+ class AssetPath < Liquid::Tag
8
8
  # @param tag_name [String] Name of the tag
9
9
  # @param asset_type [String] The type of asset to parse (js, css)
10
10
  # @param options [Hash] An options hash
@@ -17,23 +17,21 @@ module Bridgetown
17
17
  @asset_type = asset_type.strip
18
18
  end
19
19
 
20
- # Render an asset path based on the Webpack manifest file
20
+ # Render an asset path based on the frontend manifest file
21
21
  # @param context [Liquid::Context] Context passed to the tag
22
22
  #
23
23
  # @return [String] Returns "MISSING_WEBPACK_MANIFEST" if the manifest
24
- # file isn't found
24
+ # file isn't found
25
25
  # @return [String] Returns a blank string if the asset isn't found
26
26
  # @return [String] Returns the path to the asset if no issues parsing
27
- #
28
- # @raise [WebpackAssetError] if unable to find css or js in the manifest
29
- # file
30
27
  def render(context)
31
28
  @context = context
32
29
  site = context.registers[:site]
33
- Bridgetown::Utils.parse_webpack_manifest_file(site, @asset_type) || ""
30
+ Bridgetown::Utils.parse_frontend_manifest_file(site, @asset_type) || ""
34
31
  end
35
32
  end
36
33
  end
37
34
  end
38
35
 
39
- Liquid::Template.register_tag("webpack_path", Bridgetown::Tags::WebpackPath)
36
+ Liquid::Template.register_tag("asset_path", Bridgetown::Tags::AssetPath)
37
+ Liquid::Template.register_tag("webpack_path", Bridgetown::Tags::AssetPath)
@@ -15,7 +15,8 @@ namespace :frontend do
15
15
  run_process "Frontend", :yellow, "bundle exec bridgetown frontend:dev"
16
16
  end
17
17
  if sidecar
18
- sleep 4 # give Webpack time to boot before returning control to the start command
18
+ # give FE bundler time to boot before returning control to the start command
19
+ sleep Bridgetown::Utils.frontend_bundler_type == :esbuild ? 3 : 4
19
20
  else
20
21
  trap("INT") do
21
22
  Bridgetown::Utils::Aux.kill_processes
@@ -28,6 +28,22 @@ module Bridgetown
28
28
  load_path.start_with?(root_dir) && ENV["BRIDGETOWN_ENV"] != "production"
29
29
  end
30
30
 
31
+ def clear_descendants_for_reload(_cpath, value, _abspath)
32
+ unless value.is_a?(Class) && value.singleton_class < ActiveSupport::DescendantsTracker
33
+ return
34
+ end
35
+
36
+ if defined?(ActiveSupport::RubyFeatures) && ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
37
+ ActiveSupport::DescendantsTracker.clear([value.superclass])
38
+ return
39
+ end
40
+
41
+ # TODO: this could probably be refactored to work like the above
42
+ ActiveSupport::DescendantsTracker.class_variable_get(
43
+ :@@direct_descendants
44
+ )[value.superclass]&.reject! { _1 == value }
45
+ end
46
+
31
47
  def setup_loaders(autoload_paths = []) # rubocop:todo Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
32
48
  (autoload_paths.presence || config.autoload_paths).each do |load_path|
33
49
  if @loaders.key?(load_path)
@@ -49,6 +65,7 @@ module Bridgetown
49
65
 
50
66
  loader.collapse(collapsed_path)
51
67
  end
68
+ loader.on_unload(&method(:clear_descendants_for_reload)) # rubocop:disable Performance/MethodObjectAsBlock
52
69
  Bridgetown::Hooks.trigger :loader, :pre_setup, loader, load_path
53
70
  loader.setup
54
71
  loader.eager_load if config.eager_load_paths.include?(load_path)
@@ -339,19 +339,34 @@ module Bridgetown
339
339
  end
340
340
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
341
341
 
342
+ def parse_frontend_manifest_file(site, asset_type)
343
+ case frontend_bundler_type(site.root_dir)
344
+ when :webpack
345
+ parse_webpack_manifest_file(site, asset_type)
346
+ when :esbuild
347
+ parse_esbuild_manifest_file(site, asset_type)
348
+ else
349
+ Bridgetown.logger.warn(
350
+ "Frontend:",
351
+ "No frontend bundling configuration was found."
352
+ )
353
+ "MISSING_FRONTEND_BUNDLING_CONFIG"
354
+ end
355
+ end
356
+
342
357
  # Return an asset path based on the Webpack manifest file
343
358
  # @param site [Bridgetown::Site] The current site object
344
359
  # @param asset_type [String] js or css, or filename in manifest
345
360
  #
346
361
  # @return [String] Returns "MISSING_WEBPACK_MANIFEST" if the manifest
347
- # file isnt found
362
+ # file isnt found
348
363
  # @return [nil] Returns nil if the asset isnt found
349
364
  # @return [String] Returns the path to the asset if no issues parsing
350
365
  #
351
366
  # @raise [WebpackAssetError] if unable to find css or js in the manifest
352
- # file
367
+ # file
353
368
  def parse_webpack_manifest_file(site, asset_type)
354
- return log_webpack_asset_error(site, "Webpack manifest") if site.frontend_manifest.nil?
369
+ return log_frontend_asset_error(site, "Webpack manifest") if site.frontend_manifest.nil?
355
370
 
356
371
  asset_path = if %w(js css).include?(asset_type)
357
372
  site.frontend_manifest["main.#{asset_type}"]
@@ -361,11 +376,40 @@ module Bridgetown
361
376
  end&.last
362
377
  end
363
378
 
364
- return log_webpack_asset_error(site, asset_type) if asset_path.nil?
379
+ return log_frontend_asset_error(site, asset_type) if asset_path.nil?
365
380
 
366
381
  static_frontend_path site, ["js", asset_path]
367
382
  end
368
383
 
384
+ # Return an asset path based on the esbuild manifest file
385
+ # @param site [Bridgetown::Site] The current site object
386
+ # @param asset_type [String] js or css, or filename in manifest
387
+ #
388
+ # @return [String] Returns "MISSING_WEBPACK_MANIFEST" if the manifest
389
+ # file isnt found
390
+ # @return [nil] Returns nil if the asset isnt found
391
+ # @return [String] Returns the path to the asset if no issues parsing
392
+ #
393
+ # @raise [WebpackAssetError] if unable to find css or js in the manifest
394
+ # file
395
+ def parse_esbuild_manifest_file(site, asset_type) # rubocop:disable Metrics/PerceivedComplexity
396
+ return log_frontend_asset_error(site, "esbuild manifest") if site.frontend_manifest.nil?
397
+
398
+ asset_path = if %w(js css).include?(asset_type)
399
+ folder = asset_type == "js" ? "javascript" : "styles"
400
+ site.frontend_manifest["#{folder}/index.#{asset_type}"] ||
401
+ site.frontend_manifest["#{folder}/index.#{asset_type}.rb"]
402
+ else
403
+ site.frontend_manifest.find do |item, _|
404
+ item.sub(%r{^../(frontend/|src/)?}, "") == asset_type
405
+ end&.last
406
+ end
407
+
408
+ return log_frontend_asset_error(site, asset_type) if asset_path.nil?
409
+
410
+ static_frontend_path site, [asset_path]
411
+ end
412
+
369
413
  def static_frontend_path(site, additional_parts = [])
370
414
  path_parts = [
371
415
  site.base_path.gsub(%r(^/|/$), ""),
@@ -376,16 +420,26 @@ module Bridgetown
376
420
  Addressable::URI.parse(path_parts.join("/")).normalize.to_s
377
421
  end
378
422
 
379
- def log_webpack_asset_error(site, asset_type)
380
- site.data[:__webpack_asset_errors] ||= {}
381
- site.data[:__webpack_asset_errors][asset_type] ||=
423
+ def log_frontend_asset_error(site, asset_type)
424
+ site.data[:__frontend_asset_errors] ||= {}
425
+ site.data[:__frontend_asset_errors][asset_type] ||=
382
426
  Bridgetown.logger.warn(
383
- "Webpack:",
427
+ "#{frontend_bundler_type}:",
384
428
  "There was an error parsing your #{asset_type} file. \
385
429
  Please check your #{asset_type} file for any errors."
386
430
  )
387
431
 
388
- "MISSING_WEBPACK_MANIFEST_FILE"
432
+ "MISSING_#{frontend_bundler_type.upcase}_MANIFEST_FILE"
433
+ end
434
+
435
+ def frontend_bundler_type(cwd = Dir.pwd)
436
+ if File.exist?(File.join(cwd, "webpack.config.js"))
437
+ :webpack
438
+ elsif File.exist?(File.join(cwd, "esbuild.config.js"))
439
+ :esbuild
440
+ else
441
+ :unknown
442
+ end
389
443
  end
390
444
 
391
445
  def default_github_branch_name(repo_url)
@@ -401,30 +455,34 @@ module Bridgetown
401
455
  return "" unless Bridgetown.env.development? && !site.config.skip_live_reload
402
456
 
403
457
  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
- })
458
+ let lastmod = 0
459
+ function startReloadConnection() {
460
+ const evtSource = new EventSource("/_bridgetown/live_reload")
461
+ evtSource.onmessage = event => {
462
+ if (event.data == "reloaded!") {
463
+ location.reload()
418
464
  } else {
419
- if (connection_errors < 20) setTimeout(() => checkForReload(), 6000)
420
- connection_errors++
465
+ const newmod = Number(event.data)
466
+ if (lastmod > 0 && newmod > 0 && lastmod < newmod) {
467
+ location.reload()
468
+ } else {
469
+ lastmod = newmod
470
+ }
471
+ }
472
+ }
473
+ evtSource.onerror = event => {
474
+ if (evtSource.readyState === 2) {
475
+ // reconnect with new object
476
+ evtSource.close()
477
+ console.warn("Live reload: attempting to reconnect in 3 seconds...")
478
+
479
+ setTimeout(() => startReloadConnection(), 3000)
421
480
  }
422
- }).catch((err) => {
423
- if (connection_errors < 20) setTimeout(() => checkForReload(), 6000)
424
- connection_errors++
425
- })
481
+ }
426
482
  }
427
- checkForReload()
483
+ setTimeout(() => {
484
+ startReloadConnection()
485
+ }, 500)
428
486
  JAVASCRIPT
429
487
 
430
488
  %(<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.alpha9"
4
+ VERSION = "1.0.0.beta2"
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.
9
- #
10
- # site - The current site instance
11
- # options - A Hash containing the site configuration
7
+ class << self
8
+ attr_accessor :shutdown
9
+ end
10
+
11
+ # Continuously watch for file changes and rebuild the site whenever a change is detected.
12
12
  #
13
- # Returns nothing.
13
+ # @param site [Bridgetown::Site] the current site instance
14
+ # @param options [Bridgetown::Configuration] the site configuration
14
15
  def watch(site, options)
15
16
  ENV["LISTEN_GEM_DEBUGGING"] ||= "1" if options["verbose"]
16
17
 
17
- listener = build_listener(site, options)
18
- listener.start
18
+ listen(site, options)
19
19
 
20
- Bridgetown.logger.info "Auto-regeneration:", "enabled." unless options[:using_puma]
20
+ Bridgetown.logger.info "Watcher:", "enabled." unless options[:using_puma]
21
21
 
22
- unless options[:serving]
23
- trap("INT") do
24
- listener.stop
25
- Bridgetown.logger.info "", "Halting auto-regeneration."
26
- exit 0
27
- end
22
+ return if options[:serving]
28
23
 
29
- sleep_forever
24
+ trap("INT") do
25
+ self.shutdown = true
30
26
  end
31
- rescue ThreadError
32
- # You pressed Ctrl-C, oh my!
33
- end
34
27
 
35
- private
36
-
37
- def build_listener(site, options)
38
- webpack_path = site.in_root_dir(".bridgetown-webpack")
39
- 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
28
+ sleep_forever
29
+ end
43
30
 
44
- paths_to_watch = (plugin_paths_to_watch + options.autoload_paths).uniq
31
+ # Return a list of load paths which should be watched for changes
32
+ #
33
+ # @param (see #watch)
34
+ def load_paths_to_watch(site, options)
35
+ site.plugin_manager.plugins_path.select { |path| Dir.exist?(path) }
36
+ .then do |paths|
37
+ (paths + options.autoload_paths).uniq
38
+ end
39
+ end
45
40
 
41
+ # Start a listener to watch for changes and call {#reload_site}
42
+ #
43
+ # @param (see #watch)
44
+ def listen(site, options)
45
+ bundling_path = site.frontend_bundling_path
46
+ FileUtils.mkdir_p(bundling_path)
46
47
  Listen.to(
47
48
  options["source"],
48
- webpack_path,
49
- *paths_to_watch,
49
+ bundling_path,
50
+ *load_paths_to_watch(site, options),
50
51
  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
52
+ force_polling: options["force_polling"]
53
+ ) do |modified, added, removed|
59
54
  c = modified + added + removed
60
55
  n = c.length
61
56
 
62
57
  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..] }
58
+ Bridgetown.logger.info(
59
+ "Reloading…",
60
+ "#{n} file#{"s" if c.length > 1} changed at #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
61
+ )
62
+ c.each { |path| Bridgetown.logger.info "", "- #{path["#{site.root_dir}/".length..]}" }
66
63
  end
67
64
 
68
- process(site, t, options)
65
+ reload_site(site, options, paths: c)
66
+ end.start
67
+ end
68
+
69
+ # Reload the site including plugins and Zeitwerk autoloaders and process it (unless SSR)
70
+ #
71
+ # @param site [Bridgetown::Site] the current site instance
72
+ # @param options [Bridgetown::Configuration] the site configuration
73
+ # @param paths Array<String>
74
+ def reload_site(site, options, paths: []) # rubocop:todo Metrics/MethodLength
75
+ begin
76
+ time = Time.now
77
+ I18n.reload! # make sure any locale files get read again
78
+ Bridgetown::Current.site = site # needed in SSR mode apparently
79
+ catch :halt do
80
+ Bridgetown::Hooks.trigger :site, :pre_reload, site, paths
81
+ Bridgetown::Hooks.clear_reloadable_hooks
82
+ site.plugin_manager.reload_plugin_files
83
+ site.loaders_manager.reload_loaders
84
+ Bridgetown::Hooks.trigger :site, :post_reload, site, paths
85
+
86
+ if site.ssr?
87
+ site.reset(soft: true)
88
+ return
89
+ end
90
+
91
+ site.process
92
+ end
93
+ Bridgetown.logger.info "Done! 🎉", "#{"Completed".bold.green} in less than" \
94
+ " #{(Time.now - time).ceil(2)} seconds."
95
+ rescue StandardError => e
96
+ Bridgetown::Errors.print_build_error(e, trace: options[:trace])
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
@@ -124,34 +155,7 @@ module Bridgetown
124
155
  end
125
156
 
126
157
  def sleep_forever
127
- loop { sleep 1000 }
128
- 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 ""
158
+ sleep 0.5 until shutdown
155
159
  end
156
160
  end
157
161
  end
@@ -268,3 +268,4 @@ loader = Zeitwerk::Loader.new
268
268
  loader.push_dir File.join(__dir__, "bridgetown-core/model"), namespace: Bridgetown::Model
269
269
  loader.push_dir File.join(__dir__, "bridgetown-core/resource"), namespace: Bridgetown::Resource
270
270
  loader.setup # ready!
271
+ Bridgetown::Model::Origin # this needs to load first
@@ -1,23 +1,29 @@
1
1
  source "https://rubygems.org"
2
2
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
- # Hello! This is where you manage which Bridgetown version is used to run.
5
- # When you want to use a different version, change it below, save the
6
- # file and run `bundle install`. Run Bridgetown like so:
4
+ ####
5
+ # Welcome to your project's Gemfile, used by Rubygems & Bundler.
7
6
  #
8
- # bin/bridgetown start (or console, etc.)
7
+ # To install a plugin, run:
9
8
  #
10
- # This will help ensure the proper Bridgetown version is running.
9
+ # bundle add new-plugin-name -g bridgetown_plugins
10
+ #
11
+ # This will ensure the plugin is added to the correct Bundler group.
11
12
  #
12
- # To install a plugin, simply run bundle add and specify the group
13
- # "bridgetown_plugins". For example:
13
+ # When you run Bridgetown commands, we recommend using a binstub like so:
14
14
  #
15
- # bundle add some-new-plugin -g bridgetown_plugins
15
+ # bin/bridgetown start (or console, etc.)
16
16
  #
17
- # Happy Bridgetowning!
17
+ # This will help ensure the proper Bridgetown version is running.
18
+ ####
18
19
 
20
+ # If you need to upgrade/switch Bridgetown versions, change the line below
21
+ # and then run `bundle update bridgetown`
19
22
  gem "bridgetown", "~> <%= Bridgetown::VERSION %>"
20
23
 
21
- # Puma is a Rack-compatible server
24
+ # Uncomment to add file-based dynamic routing to your project:
25
+ # gem "bridgetown-routes", "~> <%= Bridgetown::VERSION %>", group: :bridgetown_plugins
26
+
27
+ # Puma is a Rack-compatible server used by Bridgetown
22
28
  # (you can optionally limit this to the "development" group)
23
- gem "puma", "~> 5.2"
29
+ gem "puma", "~> 5.5"
@@ -35,7 +35,7 @@ bundle install && yarn install
35
35
 
36
36
  To start your site in development mode, run `bin/bridgetown start` and navigate to [localhost:4000](https://localhost:4000/)!
37
37
 
38
- Use a [theme](https://github.com/topics/bridgetown-theme), add some [plugins](https://www.bridgetownrb.com/plugins/), and/or run some [automations](https://github.com/topics/bridgetown-automation) to get started quickly.
38
+ Use a [theme](https://github.com/topics/bridgetown-theme) or add some [plugins](https://www.bridgetownrb.com/plugins/) to get started quickly.
39
39
 
40
40
  ### Commands
41
41
 
@@ -54,7 +54,7 @@ bin/bridgetown console
54
54
 
55
55
  ## Deployment
56
56
 
57
- You can deploy Bridgetown sites on "Jamstack" hosts (Netlify, Vercel, Render, etc.) or virtually any tranditional web server by simply building and copying the output folder to your HTML root.
57
+ You can deploy Bridgetown sites on hosts like Render or Vercel as well as tranditional web servers by simply building and copying the output folder to your HTML root.
58
58
 
59
59
  > Read the [Bridgetown Deployment Documentation](https://www.bridgetownrb.com/docs/deployment) for more information.
60
60