Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Significant refactor of the Routes plugin #882

Merged
merged 5 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def ssr?

def enable_ssr
Bridgetown.logger.info "SSR:", "enabled."
config.fast_refresh = false # SSR mode and Fast Refresh mode are mututally exclusive
@ssr_enabled = true
end

Expand All @@ -46,6 +47,7 @@ def ssr_setup(&block)
end

def ssr_first_read
# TODO: this shouldn't be running twice, right?!
Bridgetown::Hooks.trigger :site, :pre_read, self
defaults_reader.tap do |d|
d.path_defaults.clear
Expand Down
2 changes: 0 additions & 2 deletions bridgetown-core/lib/bridgetown-core/rack/boot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
require "zeitwerk"
require "roda"
require "json"
require "roda/plugins/public"

Bridgetown::Current.preloaded_configuration ||= Bridgetown.configuration

require_relative "logger"
require_relative "routes"
require_relative "static_indexes"

module Bridgetown
module Rack
Expand Down
29 changes: 2 additions & 27 deletions bridgetown-core/lib/bridgetown-core/rack/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,36 +112,11 @@ def merge(roda_app)
new(roda_app).handle_routes
end

# Start the Roda app request cycle. There are two different code paths
# depending on if there's a site `base_path` configured
# Set up live reload if allowed, then run through all the Routes blocks.
#
# @param roda_app [Roda]
# @return [void]
def start!(roda_app)
if Bridgetown::Current.preloaded_configuration.base_path == "/"
load_all_routes roda_app
return
end

# Support custom base_path configurations
roda_app.request.on(
Bridgetown::Current.preloaded_configuration.base_path.delete_prefix("/")
) do
load_all_routes roda_app
end

nil
end

# Run the Roda public plugin first, set up live reload if allowed, then
# run through all the Routes blocks. If the file-based router plugin
# is available, kick off that request process next.
#
# @param roda_app [Roda]
# @return [void]
def load_all_routes(roda_app)
roda_app.request.public

def load_all(roda_app)
if Bridgetown.env.development? &&
!Bridgetown::Current.preloaded_configuration.skip_live_reload
setup_live_reload roda_app
Expand Down
31 changes: 0 additions & 31 deletions bridgetown-core/lib/bridgetown-core/rack/static_indexes.rb

This file was deleted.

52 changes: 45 additions & 7 deletions bridgetown-core/lib/roda/plugins/bridgetown_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def self.load_dependencies(app) # rubocop:disable Metrics
"plugin"
end

app.extend ClassMethods # we need to do this here because Roda hasn't done it yet
app.plugin :initializers
app.plugin :method_override
app.plugin :all_verbs
Expand All @@ -20,7 +21,7 @@ def self.load_dependencies(app) # rubocop:disable Metrics
app.plugin :json_parser
app.plugin :indifferent_params
app.plugin :cookies, path: "/"
app.plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
app.plugin :ssg, root: Bridgetown::Current.preloaded_configuration.destination
app.plugin :not_found do
output_folder = Bridgetown::Current.preloaded_configuration.destination
File.read(File.join(output_folder, "404.html"))
Expand Down Expand Up @@ -51,6 +52,7 @@ def self.load_dependencies(app) # rubocop:disable Metrics
result.transform!.output
end

# TODO: there may be a better way to do this, see `exception_page_css` instance method
ExceptionPage.class_eval do # rubocop:disable Metrics/BlockLength
def self.css
<<~CSS
Expand Down Expand Up @@ -107,8 +109,16 @@ def self.css
CSS
end
end
end

module ClassMethods
def root_hook(&block)
opts[:root_hook] = block
end
end

app.before do
module InstanceMethods
def initialize_bridgetown_context
if self.class.opts[:bridgetown_site]
# The site had previously been initialized via the bridgetown_ssr plugin
Bridgetown::Current.sites[self.class.opts[:bridgetown_site].label] =
Expand All @@ -117,11 +127,22 @@ def self.css
end
Bridgetown::Current.preloaded_configuration ||=
self.class.opts[:bridgetown_preloaded_config]
end

def initialize_bridgetown_root # rubocop:todo Metrics/AbcSize
request.root do
output_folder = Bridgetown::Current.preloaded_configuration.destination
File.read(File.join(output_folder, "index.html"))
rescue StandardError
hook_result = instance_exec(&self.class.opts[:root_hook]) if self.class.opts[:root_hook]
next hook_result if hook_result

status, headers, body = self.class.opts[:ssg_server].serving(
request, File.join(self.class.opts[:ssg_root], "index.html")
)
response_headers = response.headers
response_headers.replace(headers)

request.halt [status, response_headers, body]
rescue StandardError => e
Bridgetown.logger.debug("Root handler error: #{e.message}")
response.status = 500
"<p>ERROR: cannot find <code>index.html</code> in the output folder.</p>"
end
Expand All @@ -138,9 +159,26 @@ def cookies
_previous_roda_cookies.with_indifferent_access
end

# Starts up the Bridgetown routing system
# Start up the Bridgetown routing system
def bridgetown
Bridgetown::Rack::Routes.start!(scope)
scope.initialize_bridgetown_context
scope.initialize_bridgetown_root

# Run the static file server
ssg

# There are two different code paths depending on if there's a site `base_path` configured
if Bridgetown::Current.preloaded_configuration.base_path == "/"
Bridgetown::Rack::Routes.load_all scope
return
end

# Support custom base_path configurations
on(Bridgetown::Current.preloaded_configuration.base_path.delete_prefix("/")) do
Bridgetown::Rack::Routes.load_all scope
end

nil
end
end
end
Expand Down
72 changes: 72 additions & 0 deletions bridgetown-core/lib/roda/plugins/ssg.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require "uri"
require "rack/files"

class Roda
module RodaPlugins
# This is a simplifed and modified variant of Roda's Public core plugin. It adds additional
# functionality so that you can host an entire static site through Roda. What's necessary for
# this to work is handling "pretty" URLs, aka:
#
# /path/to/page -> /path/to/page.html or /path/to/page/index.html
# /path/to/page/ -> /path/to/page/index.html
#
# It does not support serving compressed files, as that should ideally be handled through a
# proxy or CDN layer in your architecture.
module SSG
PARSER = URI::DEFAULT_PARSER

def self.configure(app, opts = {})
app.opts[:ssg_root] = app.expand_path(opts.fetch(:root, "public"))
app.opts[:ssg_server] = Rack::Files.new(app.opts[:ssg_root])
end

module RequestMethods
def ssg
return unless is_get?

path = PARSER.unescape(real_remaining_path)
return if path.include?("\0")

server = roda_class.opts[:ssg_server]
path = File.join(server.root, *segments_for_path(path))

return unless File.file?(path)

status, headers, body = server.serving(self, path)
response_headers = response.headers
response_headers.replace(headers)
halt [status, response_headers, body]
end

# TODO: this could be refactored a bit
def segments_for_path(path) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
segments = []

path.split("/").each do |seg|
next if seg.empty? || seg == "."

seg == ".." ? segments.pop : segments << seg
end

path = File.join(roda_class.opts[:ssg_root], *segments)
unless File.file?(path)
path = File.join(path, "index.html")
if File.file?(path)
segments << "index.html"
else
segments[segments.size - 1] = "#{segments.last}.html"
end
end

segments
rescue IndexError
nil
end
end
end

register_plugin :ssg, SSG
end
end
1 change: 0 additions & 1 deletion bridgetown-core/lib/site_template/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ output

# Dependency folders
node_modules
vendor

# Caches
.sass-cache
Expand Down
Loading