bridgetown-core 1.2.0 → 1.3.0.beta1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 599f0ff57d4bac2146e5cacb12ccb8f2e7e02026815447c2db9cbf914ed9d3a2
4
- data.tar.gz: be29cede2daa248b4db41bb6a7eaeebb7d3533b54a5cf891922b0762a6ca47e0
3
+ metadata.gz: 9176399e9b750178476e2d57edb346989426407d5d40e8958c5be3901626f77f
4
+ data.tar.gz: a5130e5511c64acba6e6ee619a7529b49415e1c25b3e056cf4e709c4d78361da
5
5
  SHA512:
6
- metadata.gz: 739ed852e60fabf0b6b2d352c8ce8993d4751a1f6cfec67c0bb7ed4cc5f0af41bb82fb8dc3eb34f5806a421bcec38e9050cd0fbcb50249a0482f5804e19a1d27
7
- data.tar.gz: 97eea9cb28095624a8d47821f754df39790d8db46e62d79774371113c875fd914a796f3e445ad982e6f8de8e94d5babf7b8be1736c4bc93f3a9fe0a0f6862433
6
+ metadata.gz: 849bff55f1997483ce24f67536daec7bdd53ff016d7e4ab8937b0184892680eb59f159d3a3d26c22fbc63ef63486ddd1b793943cd21fead6c281f0d2d9698f69
7
+ data.tar.gz: e5c317edb3ae4e232cd2a56c885b44af5375d7daeff71f385e620d402cbe57c68cf32fdc6eb13508bb92e3e8dff254cf53e59f56790631692eee092c6eaa0463
@@ -31,26 +31,25 @@ Gem::Specification.new do |s|
31
31
 
32
32
  s.required_ruby_version = ">= 2.7.0"
33
33
 
34
- s.add_runtime_dependency("activemodel", [">= 6.0", "< 8.0"])
35
- s.add_runtime_dependency("activesupport", [">= 6.0", "< 8.0"])
36
- s.add_runtime_dependency("addressable", "~> 2.4")
37
- s.add_runtime_dependency("amazing_print", "~> 1.2")
38
- s.add_runtime_dependency("colorator", "~> 1.0")
39
- s.add_runtime_dependency("erubi", "~> 1.9")
40
- s.add_runtime_dependency("faraday", "~> 1.0")
41
- s.add_runtime_dependency("faraday_middleware", "~> 1.0")
42
- s.add_runtime_dependency("hash_with_dot_access", "~> 1.2")
43
- s.add_runtime_dependency("i18n", "~> 1.0")
44
- s.add_runtime_dependency("kramdown", "~> 2.1")
45
- s.add_runtime_dependency("kramdown-parser-gfm", "~> 1.0")
46
- s.add_runtime_dependency("liquid", "~> 5.0")
47
- s.add_runtime_dependency("listen", "~> 3.0")
48
- s.add_runtime_dependency("rake", ">= 13.0")
49
- s.add_runtime_dependency("roda", "~> 3.46")
50
- s.add_runtime_dependency("rouge", "~> 3.0")
51
- s.add_runtime_dependency("serbea", "~> 1.0")
52
- s.add_runtime_dependency("thor", "~> 1.1")
53
- s.add_runtime_dependency("tilt", "~> 2.0")
54
- s.add_runtime_dependency("webrick", "~> 1.7")
55
- s.add_runtime_dependency("zeitwerk", "~> 2.5")
34
+ s.add_runtime_dependency("activemodel", [">= 6.0", "< 8.0"])
35
+ s.add_runtime_dependency("activesupport", [">= 6.0", "< 8.0"])
36
+ s.add_runtime_dependency("addressable", "~> 2.4")
37
+ s.add_runtime_dependency("amazing_print", "~> 1.2")
38
+ s.add_runtime_dependency("colorator", "~> 1.0")
39
+ s.add_runtime_dependency("erubi", "~> 1.9")
40
+ s.add_runtime_dependency("faraday", "~> 2.0")
41
+ s.add_runtime_dependency("faraday-follow_redirects", "~> 0.3")
42
+ s.add_runtime_dependency("hash_with_dot_access", "~> 1.2")
43
+ s.add_runtime_dependency("i18n", "~> 1.0")
44
+ s.add_runtime_dependency("kramdown", "~> 2.1")
45
+ s.add_runtime_dependency("kramdown-parser-gfm", "~> 1.0")
46
+ s.add_runtime_dependency("liquid", "~> 5.0")
47
+ s.add_runtime_dependency("listen", "~> 3.0")
48
+ s.add_runtime_dependency("rake", ">= 13.0")
49
+ s.add_runtime_dependency("roda", "~> 3.46")
50
+ s.add_runtime_dependency("rouge", "~> 3.0")
51
+ s.add_runtime_dependency("serbea", "~> 1.0")
52
+ s.add_runtime_dependency("thor", "~> 1.1")
53
+ s.add_runtime_dependency("tilt", "~> 2.0")
54
+ s.add_runtime_dependency("zeitwerk", "~> 2.5")
56
55
  end
@@ -282,7 +282,7 @@ module Bridgetown
282
282
  if model_is_multi_locale?(model, model_relative_path)
283
283
  # If the model specifies a locales key, use that to determine the
284
284
  # the locale of each resource, otherwise fall back to `site.config.available_locales`
285
- locales = model.locales || site.config.available_locales
285
+ locales = model.attributes[:locales] || site.config.available_locales
286
286
 
287
287
  locales.each do |locale|
288
288
  model.locale = locale.to_sym
@@ -80,6 +80,8 @@ module Bridgetown
80
80
  after_install new_site_path, args.join(" "), options
81
81
  rescue ArgumentError => e
82
82
  say_status :alert, e.message, :red
83
+ ensure
84
+ self.class.created_site_dir = nil # reset afterwards, otherwise hanging tmp dirs in test
83
85
  end
84
86
 
85
87
  protected
@@ -49,7 +49,7 @@ module Bridgetown
49
49
  priority: Bridgetown::Hooks::DEFAULT_PRIORITY,
50
50
  &block
51
51
  )
52
- Bridgetown::Hooks.register_one(owner, event, priority: priority, &block)
52
+ Bridgetown::Hooks.register_one(owner, event, priority: priority, reloadable: false, &block)
53
53
  end
54
54
 
55
55
  def source_manifest(**kwargs)
@@ -31,7 +31,7 @@ create_builder "purgecss.rb" do
31
31
  manifest = JSON.parse(File.read(manifest_file))
32
32
 
33
33
  if Bridgetown::Utils.frontend_bundler_type == :esbuild
34
- css_file = manifest["styles/index.css"].split("/").last
34
+ css_file = (manifest["styles/index.css"] || manifest["styles/index.scss"]).split("/").last
35
35
  css_path = ["output", "_bridgetown", "static", css_file].join("/")
36
36
  else
37
37
  css_file = manifest["main.css"].split("/").last
@@ -15,10 +15,13 @@ module Bridgetown
15
15
  InvalidURLError = Class.new(FatalException)
16
16
  InvalidConfigurationError = Class.new(FatalException)
17
17
 
18
- def self.print_build_error(exc, trace: false, logger: Bridgetown.logger)
18
+ def self.print_build_error(exc, trace: false, logger: Bridgetown.logger, server: false) # rubocop:disable Metrics
19
19
  logger.error "Exception raised:", exc.class.to_s.bold
20
20
  logger.error exc.message.reset_ansi
21
21
 
22
+ build_errors_file = Bridgetown.build_errors_path if !server && Bridgetown::Current.site
23
+ build_errors_data = "#{exc.class}: #{exc.message}"
24
+
22
25
  trace_args = ["-t", "--trace"]
23
26
  print_trace_msg = true
24
27
  traces = if trace || ARGV.find { |arg| trace_args.include?(arg) }
@@ -29,6 +32,12 @@ module Bridgetown
29
32
  end
30
33
  traces.each_with_index do |backtrace_line, index|
31
34
  logger.error "#{index + 1}:", backtrace_line.reset_ansi
35
+ build_errors_data << "\n#{backtrace_line}" if index < 2
36
+ end
37
+
38
+ if build_errors_file
39
+ FileUtils.mkdir_p(File.dirname(build_errors_file))
40
+ File.write(build_errors_file, build_errors_data, mode: "w")
32
41
  end
33
42
 
34
43
  return unless print_trace_msg
@@ -8,12 +8,22 @@ require "roda/plugins/public"
8
8
  Bridgetown::Current.preloaded_configuration ||= Bridgetown.configuration
9
9
 
10
10
  require_relative "logger"
11
- require_relative "roda"
12
11
  require_relative "routes"
13
12
  require_relative "static_indexes"
14
13
 
15
14
  module Bridgetown
16
15
  module Rack
16
+ class Roda < ::Roda
17
+ def self.inherited(klass)
18
+ Bridgetown::Deprecator.deprecation_message(
19
+ "The `Bridgetown::Rack::Roda' class will be removed in favor of using the " \
20
+ "`bridgetown_server' plugin in a future version"
21
+ )
22
+ super
23
+ klass.plugin :bridgetown_server
24
+ end
25
+ end
26
+
17
27
  class << self
18
28
  # @return [Bridgetown::Utils::LoadersManager]
19
29
  attr_accessor :loaders_manager
@@ -56,6 +66,8 @@ module Bridgetown
56
66
  loader.reload
57
67
  loader.eager_load
58
68
  Bridgetown::Rack::Routes.reload_subclasses
69
+ rescue SyntaxError => e
70
+ Bridgetown::Errors.print_build_error(e)
59
71
  end.start
60
72
  end
61
73
  end
@@ -20,6 +20,37 @@ module Bridgetown
20
20
  }.freeze
21
21
 
22
22
  class << self
23
+ # rubocop:disable Bridgetown/NoPutsAllowed, Metrics/MethodLength
24
+ def print_routes
25
+ # TODO: this needs to be fully documented
26
+ routes = begin
27
+ JSON.parse(
28
+ File.read(
29
+ File.join(Bridgetown::Current.preloaded_configuration.root_dir, ".routes.json")
30
+ )
31
+ )
32
+ rescue StandardError
33
+ []
34
+ end
35
+ puts
36
+ puts "Routes:"
37
+ puts "======="
38
+ if routes.blank?
39
+ puts "No routes found. Have you commented all of your routes?"
40
+ puts "Documentation: https://github.com/jeremyevans/roda-route_list#basic-usage-"
41
+ end
42
+
43
+ routes.each do |route|
44
+ puts [
45
+ route["methods"]&.join("|") || "GET",
46
+ route["path"],
47
+ route["file"] ? "\n File: #{route["file"]}" : nil,
48
+ ].compact.join(" ")
49
+ end
50
+ puts
51
+ end
52
+ # rubocop:enable Bridgetown/NoPutsAllowed, Metrics/MethodLength
53
+
23
54
  # @return [Hash<String, Class(Routes)>]
24
55
  attr_accessor :tracked_subclasses
25
56
 
@@ -80,7 +111,7 @@ module Bridgetown
80
111
  # Initialize a new Routes instance and execute the route as part of the
81
112
  # Roda app request cycle
82
113
  #
83
- # @param roda_app [Bridgetown::Rack::Roda]
114
+ # @param roda_app [Roda]
84
115
  def merge(roda_app)
85
116
  return unless router_block
86
117
 
@@ -90,7 +121,7 @@ module Bridgetown
90
121
  # Start the Roda app request cycle. There are two different code paths
91
122
  # depending on if there's a site `base_path` configured
92
123
  #
93
- # @param roda_app [Bridgetown::Rack::Roda]
124
+ # @param roda_app [Roda]
94
125
  # @return [void]
95
126
  def start!(roda_app)
96
127
  if Bridgetown::Current.preloaded_configuration.base_path == "/"
@@ -112,7 +143,7 @@ module Bridgetown
112
143
  # run through all the Routes blocks. If the file-based router plugin
113
144
  # is available, kick off that request process next.
114
145
  #
115
- # @param roda_app [Bridgetown::Rack::Roda]
146
+ # @param roda_app [Roda]
116
147
  # @return [void]
117
148
  def load_all_routes(roda_app)
118
149
  roda_app.request.public
@@ -127,11 +158,12 @@ module Bridgetown
127
158
  end
128
159
  end
129
160
 
130
- # @param app [Bridgetown::Rack::Roda]
131
- def setup_live_reload(app) # rubocop:disable Metrics/AbcSize
161
+ # @param app [Roda]
162
+ def setup_live_reload(app) # rubocop:disable Metrics
132
163
  sleep_interval = 0.2
133
164
  file_to_check = File.join(Bridgetown::Current.preloaded_configuration.destination,
134
165
  "index.html")
166
+ errors_file = Bridgetown.build_errors_path
135
167
 
136
168
  app.request.get "_bridgetown/live_reload" do
137
169
  app.response["Content-Type"] = "text/event-stream"
@@ -146,6 +178,8 @@ module Bridgetown
146
178
  if @_mod < new_mod
147
179
  out << "data: reloaded!\n\n"
148
180
  break
181
+ elsif File.exist?(errors_file)
182
+ out << "event: builderror\ndata: #{File.read(errors_file).to_json}\n\n"
149
183
  else
150
184
  out << "data: #{new_mod}\n\n"
151
185
  end
@@ -157,7 +191,7 @@ module Bridgetown
157
191
  end
158
192
  end
159
193
 
160
- # @param roda_app [Bridgetown::Rack::Roda]
194
+ # @param roda_app [Roda]
161
195
  def initialize(roda_app)
162
196
  @_roda_app = roda_app
163
197
  end
@@ -11,13 +11,13 @@ module Bridgetown
11
11
 
12
12
  def read
13
13
  layout_entries.each do |layout_file|
14
- @layouts[layout_name(layout_file)] = \
14
+ @layouts[layout_name(layout_file)] =
15
15
  Layout.new(site, layout_directory, layout_file)
16
16
  end
17
17
 
18
18
  site.config.source_manifests.filter_map(&:layouts).each do |plugin_layouts|
19
19
  layout_entries(plugin_layouts).each do |layout_file|
20
- @layouts[layout_name(layout_file)] ||= \
20
+ @layouts[layout_name(layout_file)] ||=
21
21
  Layout.new(site, plugin_layouts, layout_file, from_plugin: true)
22
22
  end
23
23
  end
@@ -13,6 +13,8 @@ module Bridgetown
13
13
  @config = config
14
14
  @loaders = {}
15
15
  @root_dir = config.root_dir
16
+
17
+ FileUtils.rm_f(Bridgetown.build_errors_path)
16
18
  end
17
19
 
18
20
  def unload_loaders
@@ -75,6 +77,8 @@ module Bridgetown
75
77
  end
76
78
 
77
79
  def reload_loaders
80
+ FileUtils.rm_f(Bridgetown.build_errors_path)
81
+
78
82
  @loaders.each do |load_path, loader|
79
83
  next unless reloading_enabled?(load_path)
80
84
 
@@ -466,6 +466,7 @@ module Bridgetown
466
466
  function startReloadConnection() {
467
467
  const evtSource = new EventSource("#{site.base_path(strip_slash_only: true)}/_bridgetown/live_reload")
468
468
  evtSource.onmessage = event => {
469
+ if (document.querySelector("#bridgetown-build-error")) document.querySelector("#bridgetown-build-error").close()
469
470
  if (event.data == "reloaded!") {
470
471
  location.reload()
471
472
  } else {
@@ -477,6 +478,23 @@ module Bridgetown
477
478
  }
478
479
  }
479
480
  }
481
+ evtSource.addEventListener("builderror", event => {
482
+ let dialog = document.querySelector("#bridgetown-build-error")
483
+ if (!dialog) {
484
+ dialog = document.createElement("dialog")
485
+ dialog.id = "bridgetown-build-error"
486
+ dialog.style.borderColor = "red"
487
+ dialog.style.fontSize = "110%"
488
+ dialog.innerHTML = `
489
+ <p style="color:red">There was an error when building the site:</p>
490
+ <output><pre></pre></output>
491
+ <p><small>Check your Bridgetown logs for further details.</small></p>
492
+ `
493
+ document.body.appendChild(dialog)
494
+ dialog.showModal()
495
+ }
496
+ dialog.querySelector("pre").textContent = JSON.parse(event.data)
497
+ })
480
498
  evtSource.onerror = event => {
481
499
  if (evtSource.readyState === 2) {
482
500
  // reconnect with new object
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bridgetown
4
- VERSION = "1.2.0"
5
- CODE_NAME = "Bonny Slope"
4
+ VERSION = "1.3.0.beta1"
5
+ CODE_NAME = "Kelly Butte"
6
6
  end
@@ -106,7 +106,7 @@ module Bridgetown
106
106
  end
107
107
  Bridgetown.logger.info "Done! 🎉", "#{"Completed".bold.green} in less than " \
108
108
  "#{(Time.now - time).ceil(2)} seconds."
109
- rescue StandardError => e
109
+ rescue StandardError, SyntaxError => e
110
110
  Bridgetown::Errors.print_build_error(e, trace: options[:trace])
111
111
  end
112
112
  Bridgetown.logger.info ""
@@ -377,6 +377,18 @@ module Bridgetown
377
377
  File.join(base_directory, clean_path)
378
378
  end
379
379
  end
380
+
381
+ # When there's a build error, error details will be logged to a file which the dev server
382
+ # can read and pass along to the browser.
383
+ #
384
+ # @return [String] the path to the cached errors file
385
+ def build_errors_path
386
+ File.join(
387
+ (Bridgetown::Current.site&.config || Bridgetown::Current.preloaded_configuration).root_dir,
388
+ ".bridgetown-cache",
389
+ "build_errors.txt"
390
+ )
391
+ end
380
392
  end
381
393
  end
382
394
 
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Roda
4
+ module RodaPlugins
5
+ module BridgetownServer
6
+ SiteContext = Struct.new(:registers) # for use by Liquid-esque URL helpers
7
+
8
+ def self.load_dependencies(app) # rubocop:disable Metrics
9
+ unless Bridgetown::Current.preloaded_configuration
10
+ raise "You must supply a preloaded configuration before loading the Bridgetown Roda " \
11
+ "plugin"
12
+ end
13
+
14
+ app.plugin :initializers
15
+ app.plugin :method_override
16
+ app.plugin :all_verbs
17
+ app.plugin :hooks
18
+ app.plugin :common_logger, Bridgetown::Rack::Logger.new($stdout), method: :info
19
+ app.plugin :json
20
+ app.plugin :json_parser
21
+ app.plugin :indifferent_params
22
+ app.plugin :cookies
23
+ app.plugin :streaming
24
+ app.plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
25
+ app.plugin :not_found do
26
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
27
+ File.read(File.join(output_folder, "404.html"))
28
+ rescue Errno::ENOENT
29
+ "404 Not Found"
30
+ end
31
+ app.plugin :exception_page
32
+ app.plugin :error_handler do |e|
33
+ Bridgetown::Errors.print_build_error(
34
+ e, logger: Bridgetown::LogAdapter.new(self.class.opts[:common_logger]), server: true
35
+ )
36
+ next exception_page(e) if ENV.fetch("RACK_ENV", nil) == "development"
37
+
38
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
39
+ File.read(File.join(output_folder, "500.html"))
40
+ rescue Errno::ENOENT
41
+ "500 Internal Server Error"
42
+ end
43
+
44
+ ExceptionPage.class_eval do # rubocop:disable Metrics/BlockLength
45
+ def self.css
46
+ <<~CSS
47
+ html * { padding:0; margin:0; }
48
+ body * { padding:10px 20px; }
49
+ body * * { padding:0; }
50
+ body { font-family: -apple-system, sans-serif; font-size: 90%; }
51
+ body>div { border-bottom:1px solid #ddd; }
52
+ code { font-family: ui-monospace, monospace; }
53
+ h1 { font-weight: bold; margin-block-end: .8em; }
54
+ h2 { margin-block-end:.8em; }
55
+ h2 span { font-size:80%; color:#f7f7db; font-weight:normal; }
56
+ h3 { margin:1em 0 .5em 0; }
57
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
58
+ table {
59
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
60
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
61
+ thead th {
62
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
63
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
64
+ tbody th { text-align:right; opacity: 0.7; padding-right:.5em; }
65
+ table.vars { margin:5px 0 2px 40px; }
66
+ table.vars td, table.req td { font-family: ui-monospace, monospace; }
67
+ table td.code { width:100%;}
68
+ table td.code div { overflow:hidden; }
69
+ table.source th { color:#666; }
70
+ table.source td {
71
+ font-family: ui-monospace, monospace; white-space:pre; border-bottom:1px solid #eee; }
72
+ ul.traceback { list-style-type:none; }
73
+ ul.traceback li.frame { margin-bottom:1em; }
74
+ div.context { margin: 10px 0; }
75
+ div.context ol {
76
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
77
+ div.context ol li {
78
+ font-family: ui-monospace, monospace; white-space:pre; color:#666; cursor:pointer; }
79
+ div.context ol.context-line li { color:black; background-color:#f7f7db; }
80
+ div.context ol.context-line li span { float: right; }
81
+ div.commands { margin-left: 40px; }
82
+ div.commands a { color:black; text-decoration:none; }
83
+ #summary { background: #1D453C; color: white; }
84
+ #summary h2 { font-weight: normal; color: white; }
85
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
86
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
87
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
88
+ #summary a { color: #f47c3c; }
89
+ #explanation { background:#eee; }
90
+ #traceback { background: white; }
91
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
92
+ #summary table { border:none; background:transparent; }
93
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
94
+ #requestinfo h3 { margin-bottom:-1em; }
95
+ .error { background: #ffc; }
96
+ .specific { color:#cc3300; font-weight:bold; }
97
+ CSS
98
+ end
99
+ end
100
+
101
+ app.before do
102
+ if self.class.opts[:bridgetown_site]
103
+ # The site had previously been initialized via the bridgetown_ssr plugin
104
+ Bridgetown::Current.sites[self.class.opts[:bridgetown_site].label] =
105
+ self.class.opts[:bridgetown_site]
106
+ @context ||= SiteContext.new({ site: self.class.opts[:bridgetown_site] })
107
+ end
108
+ Bridgetown::Current.preloaded_configuration ||=
109
+ self.class.opts[:bridgetown_preloaded_config]
110
+
111
+ request.root do
112
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
113
+ File.read(File.join(output_folder, "index.html"))
114
+ rescue StandardError
115
+ response.status = 500
116
+ "<p>ERROR: cannot find <code>index.html</code> in the output folder.</p>"
117
+ end
118
+ end
119
+ end
120
+
121
+ Roda::RodaRequest.alias_method :_previous_roda_cookies, :cookies
122
+
123
+ module RequestMethods
124
+ # Monkeypatch Roda/Rack's Request object so it returns a hash which allows for
125
+ # indifferent access
126
+ def cookies
127
+ # TODO: maybe replace with a simpler hash that offers an overloaded `[]` method
128
+ _previous_roda_cookies.with_indifferent_access
129
+ end
130
+
131
+ # Starts up the Bridgetown routing system
132
+ def bridgetown
133
+ Bridgetown::Rack::Routes.start!(scope)
134
+ end
135
+ end
136
+ end
137
+
138
+ register_plugin :bridgetown_server, BridgetownServer
139
+ end
140
+ end
@@ -24,10 +24,13 @@ gem "bridgetown", "~> <%= Bridgetown::VERSION %>"
24
24
  # Uncomment to add file-based dynamic routing to your project:
25
25
  # gem "bridgetown-routes", "~> <%= Bridgetown::VERSION %>"
26
26
 
27
+ # Puma is the Rack-compatible web server used by Bridgetown
28
+ # (you can optionally limit this to the "development" group)
29
+ gem "puma", "< 7"
30
+
27
31
  # Uncomment to use the Inspectors API to manipulate the output
28
32
  # of your HTML or XML resources:
29
33
  # gem "nokogiri", "~> 1.13"
30
34
 
31
- # Puma is a Rack-compatible server used by Bridgetown
32
- # (you can optionally limit this to the "development" group)
33
- gem "puma", "~> 5.6"
35
+ # Or for faster parsing of HTML-only resources via Inspectors, use Nokolexbor:
36
+ # gem "nokolexbor", "~> 0.4"
@@ -2,9 +2,11 @@
2
2
  # on the concept of a routing tree. Bridgetown uses it for its development
3
3
  # server, but you can also run it in production for fast, dynamic applications.
4
4
  #
5
- # Learn more at: http://roda.jeremyevans.net
5
+ # Learn more at: https://www.bridgetownrb.com/docs/routes
6
+
7
+ class RodaApp < Roda
8
+ plugin :bridgetown_server
6
9
 
7
- class RodaApp < Bridgetown::Rack::Roda
8
10
  # Some Roda configuration is handled in the `config/initializers.rb` file.
9
11
  # But you can also add additional Roda configuration here if needed.
10
12
 
@@ -5,7 +5,7 @@ date: <%= Time.now.strftime('%Y-%m-%d %H:%M:%S %z') %>
5
5
  categories: updates
6
6
  ---
7
7
 
8
- You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `bridgetown serve`, which launches a web server and auto-regenerates your site when a file is updated.
8
+ You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `bin/bridgetown start`, which launches a web server and auto-regenerates your site when a file is updated.
9
9
 
10
10
  Bridgetown requires blog post files to be named according to the following format:
11
11
 
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.2.0
4
+ version: 1.3.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bridgetown Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-25 00:00:00.000000000 Z
11
+ date: 2023-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -112,28 +112,28 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: '1.0'
115
+ version: '2.0'
116
116
  type: :runtime
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: '1.0'
122
+ version: '2.0'
123
123
  - !ruby/object:Gem::Dependency
124
- name: faraday_middleware
124
+ name: faraday-follow_redirects
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: '1.0'
129
+ version: '0.3'
130
130
  type: :runtime
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: '1.0'
136
+ version: '0.3'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: hash_with_dot_access
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -302,20 +302,6 @@ dependencies:
302
302
  - - "~>"
303
303
  - !ruby/object:Gem::Version
304
304
  version: '2.0'
305
- - !ruby/object:Gem::Dependency
306
- name: webrick
307
- requirement: !ruby/object:Gem::Requirement
308
- requirements:
309
- - - "~>"
310
- - !ruby/object:Gem::Version
311
- version: '1.7'
312
- type: :runtime
313
- prerelease: false
314
- version_requirements: !ruby/object:Gem::Requirement
315
- requirements:
316
- - - "~>"
317
- - !ruby/object:Gem::Version
318
- version: '1.7'
319
305
  - !ruby/object:Gem::Dependency
320
306
  name: zeitwerk
321
307
  requirement: !ruby/object:Gem::Requirement
@@ -368,8 +354,6 @@ files:
368
354
  - lib/bridgetown-core/commands/new.rb
369
355
  - lib/bridgetown-core/commands/plugins.rb
370
356
  - lib/bridgetown-core/commands/registrations.rb
371
- - lib/bridgetown-core/commands/serve.rb
372
- - lib/bridgetown-core/commands/serve/servlet.rb
373
357
  - lib/bridgetown-core/commands/start.rb
374
358
  - lib/bridgetown-core/commands/webpack.rb
375
359
  - lib/bridgetown-core/commands/webpack/enable-postcss.rb
@@ -487,7 +471,6 @@ files:
487
471
  - lib/bridgetown-core/plugin_manager.rb
488
472
  - lib/bridgetown-core/rack/boot.rb
489
473
  - lib/bridgetown-core/rack/logger.rb
490
- - lib/bridgetown-core/rack/roda.rb
491
474
  - lib/bridgetown-core/rack/routes.rb
492
475
  - lib/bridgetown-core/rack/static_indexes.rb
493
476
  - lib/bridgetown-core/reader.rb
@@ -529,7 +512,7 @@ files:
529
512
  - lib/bridgetown-core/version.rb
530
513
  - lib/bridgetown-core/watcher.rb
531
514
  - lib/bridgetown-core/yaml_parser.rb
532
- - lib/roda/plugins/bridgetown_boot.rb
515
+ - lib/roda/plugins/bridgetown_server.rb
533
516
  - lib/roda/plugins/bridgetown_ssr.rb
534
517
  - lib/roda/plugins/initializers.rb
535
518
  - lib/roda/plugins/method_override.rb
@@ -601,9 +584,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
601
584
  version: 2.7.0
602
585
  required_rubygems_version: !ruby/object:Gem::Requirement
603
586
  requirements:
604
- - - ">="
587
+ - - ">"
605
588
  - !ruby/object:Gem::Version
606
- version: '0'
589
+ version: 1.3.1
607
590
  requirements: []
608
591
  rubygems_version: 3.1.4
609
592
  signing_key:
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "webrick"
4
-
5
- module Bridgetown
6
- module Commands
7
- class Serve
8
- class Servlet < WEBrick::HTTPServlet::FileHandler
9
- DEFAULTS = {
10
- "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
11
- "no-store, no-cache, must-revalidate",
12
- }.freeze
13
-
14
- def initialize(server, root, callbacks)
15
- # So we can access them easily.
16
- @bridgetown_opts = server.config[:BridgetownOptions]
17
- set_defaults
18
- super
19
- end
20
-
21
- def search_index_file(req, res)
22
- super ||
23
- search_file(req, res, ".html") ||
24
- search_file(req, res, ".xhtml")
25
- end
26
-
27
- # Add the ability to tap file.html the same way that Nginx does on our
28
- # Docker images (or on GitHub Pages.) The difference is that we might end
29
- # up with a different preference on which comes first.
30
-
31
- def search_file(req, res, basename)
32
- # /file.* > /file/index.html > /file.html
33
- super ||
34
- super(req, res, "#{basename}.html") ||
35
- super(req, res, "#{basename}.xhtml")
36
- end
37
-
38
- # rubocop:disable Naming/MethodName
39
- def do_GET(req, res)
40
- rtn = super
41
-
42
- validate_and_ensure_charset(req, res)
43
- res.header.merge!(@headers)
44
- rtn
45
- end
46
- # rubocop:enable Naming/MethodName
47
-
48
- private
49
-
50
- def validate_and_ensure_charset(_req, res)
51
- key = res.header.keys.grep(%r!content-type!i).first
52
- typ = res.header[key]
53
-
54
- return if %r!;\s*charset=!.match?(typ)
55
-
56
- res.header[key] = "#{typ}; charset=#{@bridgetown_opts["encoding"]}"
57
- end
58
-
59
- def set_defaults
60
- hash_ = @bridgetown_opts.fetch("webrick", {}).fetch("headers", {})
61
- DEFAULTS.each_with_object(@headers = hash_) do |(key, val), hash|
62
- hash[key] = val unless hash.key?(key)
63
- end
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,253 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bridgetown
4
- module Commands
5
- class Serve < Thor::Group
6
- extend BuildOptions
7
- extend Summarizable
8
- include ConfigurationOverridable
9
-
10
- Registrations.register do
11
- register(Serve, "serve", "serve", Serve.summary)
12
- end
13
-
14
- class_option :host, aliases: "-H", desc: "Host to bind to"
15
- class_option :port, aliases: "-P", desc: "Port to listen on"
16
- class_option :detach,
17
- aliases: "-B",
18
- type: :boolean,
19
- desc: "Run the server in the background"
20
- class_option :ssl_cert, desc: "X.509 (SSL) certificate."
21
- class_option :ssl_key, desc: "X.509 (SSL) Private Key."
22
- class_option :show_dir_listing,
23
- type: :boolean,
24
- desc: "Show a directory listing instead of loading your index file."
25
- class_option :skip_initial_build,
26
- type: :boolean,
27
- desc: "Skips the initial site build which occurs before the server is started."
28
- class_option :watch,
29
- type: :boolean,
30
- aliases: "-w",
31
- default: true,
32
- desc: "Watch for changes and rebuild"
33
-
34
- def self.banner
35
- "bridgetown serve [options]"
36
- end
37
- summary "DEPRECATED (Serve your site locally using WEBrick)"
38
-
39
- DIRECTORY_INDEX = %w(
40
- index.htm
41
- index.html
42
- index.rhtml
43
- index.xht
44
- index.xhtml
45
- index.cgi
46
- index.xml
47
- index.json
48
- ).freeze
49
-
50
- def serve
51
- Bridgetown::Deprecator.deprecation_message(
52
- "WEBrick (serve) will be removed in favor of Puma (start) in the next Bridgetown release"
53
- )
54
-
55
- @mutex = Mutex.new
56
- @run_cond = ConditionVariable.new
57
- @running = false
58
-
59
- no_watch = options["watch"] == false
60
-
61
- options = Thor::CoreExt::HashWithIndifferentAccess.new(self.options)
62
- options["serving"] = true
63
- options["watch"] = true unless no_watch
64
-
65
- config = configuration_with_overrides(options, Bridgetown::Current.preloaded_configuration)
66
- if Bridgetown.environment == "development"
67
- default_url(config).tap do |url|
68
- options["url"] = url
69
- config.url = url
70
- end
71
- end
72
-
73
- invoke(Build, [], options)
74
- start_server
75
- end
76
-
77
- protected
78
-
79
- def start_server
80
- destination = Bridgetown::Current.preloaded_configuration.destination
81
- setup(destination)
82
-
83
- start_up_webrick(destination)
84
- end
85
-
86
- def setup(destination)
87
- require_relative "serve/servlet"
88
-
89
- FileUtils.mkdir_p(destination)
90
- return unless File.exist?(File.join(destination, "404.html"))
91
-
92
- WEBrick::HTTPResponse.class_eval do
93
- def create_error_page
94
- @header["Content-Type"] = "text/html; charset=UTF-8"
95
- @body = File.read(File.join(@config[:DocumentRoot], "404.html"))
96
- end
97
- end
98
- end
99
-
100
- def webrick_opts(opts)
101
- opts = {
102
- BridgetownOptions: opts,
103
- DoNotReverseLookup: true,
104
- MimeTypes: mime_types,
105
- DocumentRoot: opts["destination"],
106
- StartCallback: start_callback(opts["detach"]),
107
- StopCallback: stop_callback(opts["detach"]),
108
- BindAddress: opts["host"],
109
- Port: opts["port"],
110
- DirectoryIndex: DIRECTORY_INDEX,
111
- }
112
-
113
- opts[:DirectoryIndex] = [] if opts[:BridgetownOptions]["show_dir_listing"]
114
-
115
- enable_ssl(opts)
116
- enable_logging(opts)
117
- opts
118
- end
119
-
120
- def start_up_webrick(destination)
121
- opts = Bridgetown::Current.preloaded_configuration
122
- @server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
123
- @server.mount(opts["base_path"].to_s, Servlet, destination, file_handler_opts)
124
-
125
- Bridgetown.logger.info "Server address:", server_address(@server, opts)
126
- launch_browser @server, opts if opts["open_url"]
127
- boot_or_detach @server, opts
128
- end
129
-
130
- def shutdown
131
- @server.shutdown if running?
132
- end
133
-
134
- def default_url(config)
135
- format_url(
136
- config["ssl_cert"] && config["ssl_key"],
137
- config["host"] == "127.0.0.1" ? "localhost" : config["host"],
138
- config["port"]
139
- )
140
- end
141
-
142
- def format_url(ssl_enabled, address, port, baseurl = nil)
143
- format("%<prefix>s://%<address>s:%<port>i%<baseurl>s",
144
- prefix: ssl_enabled ? "https" : "http",
145
- address: address,
146
- port: port,
147
- baseurl: baseurl ? "#{baseurl}/" : "")
148
- end
149
-
150
- # Recreate NondisclosureName under utf-8 circumstance
151
- def file_handler_opts
152
- WEBrick::Config::FileHandler.merge(
153
- FancyIndexing: true,
154
- NondisclosureName: [
155
- ".ht*", "~*",
156
- ]
157
- )
158
- end
159
-
160
- def server_address(server, options = {})
161
- format_url(
162
- server.config[:SSLEnable],
163
- server.config[:BindAddress],
164
- server.config[:Port],
165
- options["baseurl"]
166
- )
167
- end
168
-
169
- # Keep in our area with a thread or detach the server as requested
170
- # by the user. This method determines what we do based on what you
171
- # ask us to do.
172
- def boot_or_detach(server, opts)
173
- if opts["detach"]
174
- pid = Process.fork do
175
- server.start
176
- end
177
-
178
- Process.detach(pid)
179
- Bridgetown.logger.info "Server detached with pid '#{pid}'.", \
180
- "Run `pkill -f bridgetown' or `kill -9 #{pid}' " \
181
- "to stop the server."
182
- else
183
- t = Thread.new { server.start }
184
- trap("INT") { server.shutdown }
185
- t.join
186
- end
187
- end
188
-
189
- # Make the stack verbose if the user requests it.
190
- def enable_logging(opts)
191
- opts[:AccessLog] = []
192
- level = WEBrick::Log.const_get(opts[:BridgetownOptions]["verbose"] ? :DEBUG : :WARN)
193
- opts[:Logger] = WEBrick::Log.new($stdout, level)
194
- end
195
-
196
- # Add SSL to the stack if the user triggers --enable-ssl and they
197
- # provide both types of certificates commonly needed. Raise if they
198
- # forget to add one of the certificates.
199
- def enable_ssl(opts)
200
- cert, key, src =
201
- opts[:BridgetownOptions].values_at("ssl_cert", "ssl_key", "source")
202
-
203
- return if cert.nil? && key.nil?
204
- raise "Missing --ssl_cert or --ssl_key. Both are required." unless cert && key
205
-
206
- require "openssl"
207
- require "webrick/https"
208
-
209
- opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert))
210
- begin
211
- opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
212
- rescue StandardError
213
- raise unless defined?(OpenSSL::PKey::EC)
214
-
215
- opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key))
216
- end
217
- opts[:SSLEnable] = true
218
- end
219
-
220
- def start_callback(detached)
221
- return if detached
222
-
223
- proc do
224
- @mutex.synchronize do
225
- @running = true
226
- Bridgetown.logger.info("Server running…", "press ctrl-c to stop.")
227
- @run_cond.broadcast
228
- end
229
- end
230
- end
231
-
232
- def stop_callback(detached)
233
- return if detached
234
-
235
- proc do
236
- @mutex.synchronize do
237
- @running = false
238
- @run_cond.broadcast
239
- end
240
- end
241
- end
242
-
243
- def mime_types
244
- file = File.expand_path("../mime.types", __dir__)
245
- WEBrick::HTTPUtils.load_mime_types(file)
246
- end
247
-
248
- def read_file(source_dir, file_path)
249
- File.read(Bridgetown.sanitized_path(source_dir, file_path))
250
- end
251
- end
252
- end
253
- end
@@ -1,157 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- unless Bridgetown::Current.preloaded_configuration
4
- raise "You must supply a preloaded configuration before loading Bridgetown's Roda superclass"
5
- end
6
-
7
- module Bridgetown
8
- module Rack
9
- class Roda < ::Roda
10
- class << self
11
- def inherited(klass)
12
- super
13
- klass.plugin :initializers
14
- end
15
-
16
- # rubocop:disable Bridgetown/NoPutsAllowed
17
- def print_routes
18
- # TODO: this needs to be fully documented
19
- routes = begin
20
- JSON.parse(
21
- File.read(
22
- File.join(Bridgetown::Current.preloaded_configuration.root_dir, ".routes.json")
23
- )
24
- )
25
- rescue StandardError
26
- []
27
- end
28
- puts
29
- puts "Routes:"
30
- puts "======="
31
- if routes.blank?
32
- puts "No routes found. Have you commented all of your routes?"
33
- puts "Documentation: https://github.com/jeremyevans/roda-route_list#basic-usage-"
34
- end
35
-
36
- routes.each do |route|
37
- puts [
38
- route["methods"]&.join("|") || "GET",
39
- route["path"],
40
- route["file"] ? "\n File: #{route["file"]}" : nil,
41
- ].compact.join(" ")
42
- end
43
- puts
44
- end
45
- # rubocop:enable Bridgetown/NoPutsAllowed
46
- end
47
-
48
- SiteContext = Struct.new(:registers) # for use by Liquid-esque URL helpers
49
-
50
- plugin :method_override
51
- plugin :all_verbs
52
- plugin :hooks
53
- plugin :common_logger, Bridgetown::Rack::Logger.new($stdout), method: :info
54
- plugin :json
55
- plugin :json_parser
56
- plugin :indifferent_params
57
- plugin :cookies
58
- plugin :streaming
59
- plugin :bridgetown_boot
60
- plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
61
- plugin :not_found do
62
- output_folder = Bridgetown::Current.preloaded_configuration.destination
63
- File.read(File.join(output_folder, "404.html"))
64
- rescue Errno::ENOENT
65
- "404 Not Found"
66
- end
67
- plugin :exception_page
68
- plugin :error_handler do |e|
69
- Bridgetown::Errors.print_build_error(
70
- e, logger: Bridgetown::LogAdapter.new(self.class.opts[:common_logger])
71
- )
72
- next exception_page(e) if ENV.fetch("RACK_ENV", nil) == "development"
73
-
74
- output_folder = Bridgetown::Current.preloaded_configuration.destination
75
- File.read(File.join(output_folder, "500.html"))
76
- rescue Errno::ENOENT
77
- "500 Internal Server Error"
78
- end
79
-
80
- ::Roda::RodaPlugins::ExceptionPage.class_eval do
81
- def self.css
82
- <<~CSS
83
- html * { padding:0; margin:0; }
84
- body * { padding:10px 20px; }
85
- body * * { padding:0; }
86
- body { font-family: -apple-system, sans-serif; font-size: 90%; }
87
- body>div { border-bottom:1px solid #ddd; }
88
- code { font-family: ui-monospace, monospace; }
89
- h1 { font-weight: bold; margin-block-end: .8em; }
90
- h2 { margin-block-end:.8em; }
91
- h2 span { font-size:80%; color:#f7f7db; font-weight:normal; }
92
- h3 { margin:1em 0 .5em 0; }
93
- h4 { margin:0 0 .5em 0; font-weight: normal; }
94
- table {
95
- border:1px solid #ccc; border-collapse: collapse; background:white; }
96
- tbody td, tbody th { vertical-align:top; padding:2px 3px; }
97
- thead th {
98
- padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
99
- font-weight:normal; font-size:11px; border:1px solid #ddd; }
100
- tbody th { text-align:right; opacity: 0.7; padding-right:.5em; }
101
- table.vars { margin:5px 0 2px 40px; }
102
- table.vars td, table.req td { font-family: ui-monospace, monospace; }
103
- table td.code { width:100%;}
104
- table td.code div { overflow:hidden; }
105
- table.source th { color:#666; }
106
- table.source td {
107
- font-family: ui-monospace, monospace; white-space:pre; border-bottom:1px solid #eee; }
108
- ul.traceback { list-style-type:none; }
109
- ul.traceback li.frame { margin-bottom:1em; }
110
- div.context { margin: 10px 0; }
111
- div.context ol {
112
- padding-left:30px; margin:0 10px; list-style-position: inside; }
113
- div.context ol li {
114
- font-family: ui-monospace, monospace; white-space:pre; color:#666; cursor:pointer; }
115
- div.context ol.context-line li { color:black; background-color:#f7f7db; }
116
- div.context ol.context-line li span { float: right; }
117
- div.commands { margin-left: 40px; }
118
- div.commands a { color:black; text-decoration:none; }
119
- #summary { background: #1D453C; color: white; }
120
- #summary h2 { font-weight: normal; color: white; }
121
- #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
122
- #summary ul#quicklinks li { float: left; padding: 0 1em; }
123
- #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
124
- #summary a { color: #f47c3c; }
125
- #explanation { background:#eee; }
126
- #traceback { background: white; }
127
- #requestinfo { background:#f6f6f6; padding-left:120px; }
128
- #summary table { border:none; background:transparent; }
129
- #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
130
- #requestinfo h3 { margin-bottom:-1em; }
131
- .error { background: #ffc; }
132
- .specific { color:#cc3300; font-weight:bold; }
133
- CSS
134
- end
135
- end
136
-
137
- before do
138
- if self.class.opts[:bridgetown_site]
139
- # The site had previously been initialized via the bridgetown_ssr plugin
140
- Bridgetown::Current.sites[self.class.opts[:bridgetown_site].label] =
141
- self.class.opts[:bridgetown_site]
142
- @context ||= SiteContext.new({ site: self.class.opts[:bridgetown_site] })
143
- end
144
- Bridgetown::Current.preloaded_configuration ||=
145
- self.class.opts[:bridgetown_preloaded_config]
146
-
147
- request.root do
148
- output_folder = Bridgetown::Current.preloaded_configuration.destination
149
- File.read(File.join(output_folder, "index.html"))
150
- rescue StandardError
151
- response.status = 500
152
- "<p>ERROR: cannot find <code>index.html</code> in the output folder.</p>"
153
- end
154
- end
155
- end
156
- end
157
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Roda
4
- module RodaPlugins
5
- module BridgetownBoot
6
- Roda::RodaRequest.alias_method :_previous_roda_cookies, :cookies
7
-
8
- module RequestMethods
9
- # Monkeypatch Roda/Rack's Request object so it returns a hash which allows for
10
- # indifferent access
11
- def cookies
12
- # TODO: maybe replace with a simpler hash that offers an overloaded `[]` method
13
- _previous_roda_cookies.with_indifferent_access
14
- end
15
-
16
- # Starts up the Bridgetown routing system
17
- def bridgetown
18
- Bridgetown::Rack::Routes.start!(scope)
19
- end
20
- end
21
- end
22
-
23
- register_plugin :bridgetown_boot, BridgetownBoot
24
- end
25
- end