bridgetown-core 1.0.0 → 1.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -1
  3. data/lib/bridgetown-core/collection.rb +37 -20
  4. data/lib/bridgetown-core/commands/concerns/actions.rb +3 -2
  5. data/lib/bridgetown-core/commands/configure.rb +1 -1
  6. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +95 -12
  7. data/lib/bridgetown-core/commands/new.rb +10 -9
  8. data/lib/bridgetown-core/commands/plugins.rb +2 -0
  9. data/lib/bridgetown-core/commands/start.rb +3 -0
  10. data/lib/bridgetown-core/commands/webpack/update.rb +2 -2
  11. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +11 -2
  12. data/lib/bridgetown-core/component.rb +13 -7
  13. data/lib/bridgetown-core/concerns/localizable.rb +20 -0
  14. data/lib/bridgetown-core/concerns/prioritizable.rb +44 -0
  15. data/lib/bridgetown-core/concerns/publishable.rb +11 -1
  16. data/lib/bridgetown-core/concerns/site/configurable.rb +2 -10
  17. data/lib/bridgetown-core/concerns/site/localizable.rb +5 -1
  18. data/lib/bridgetown-core/concerns/site/ssr.rb +3 -3
  19. data/lib/bridgetown-core/concerns/site/writable.rb +28 -0
  20. data/lib/bridgetown-core/configuration.rb +2 -0
  21. data/lib/bridgetown-core/configurations/bt-postcss/postcss.config.js +5 -3
  22. data/lib/bridgetown-core/configurations/bt-postcss.rb +1 -1
  23. data/lib/bridgetown-core/configurations/lit/esbuild-plugins.js +21 -0
  24. data/lib/bridgetown-core/configurations/lit/happy-days.lit.js +26 -0
  25. data/lib/bridgetown-core/configurations/lit/lit-components-entry.js +1 -0
  26. data/lib/bridgetown-core/configurations/lit/lit-ssr.config.js +6 -0
  27. data/lib/bridgetown-core/configurations/lit.rb +95 -0
  28. data/lib/bridgetown-core/configurations/open-props/variables.css.erb +11 -0
  29. data/lib/bridgetown-core/configurations/open-props.rb +21 -0
  30. data/lib/bridgetown-core/configurations/ruby2js/hello_world.js.rb +9 -0
  31. data/lib/bridgetown-core/configurations/ruby2js.rb +67 -0
  32. data/lib/bridgetown-core/configurations/shoelace.rb +50 -0
  33. data/lib/bridgetown-core/configurations/tailwindcss.rb +16 -2
  34. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +1 -1
  35. data/lib/bridgetown-core/drops/generated_page_drop.rb +2 -1
  36. data/lib/bridgetown-core/drops/resource_drop.rb +2 -1
  37. data/lib/bridgetown-core/errors.rb +5 -5
  38. data/lib/bridgetown-core/filters/translation_filters.rb +11 -0
  39. data/lib/bridgetown-core/filters/url_filters.rb +37 -10
  40. data/lib/bridgetown-core/filters.rb +3 -0
  41. data/lib/bridgetown-core/frontmatter_defaults.rb +14 -8
  42. data/lib/bridgetown-core/generated_page.rb +1 -0
  43. data/lib/bridgetown-core/kramdown/parser/gfm.rb +36 -0
  44. data/lib/bridgetown-core/model/base.rb +1 -2
  45. data/lib/bridgetown-core/plugin.rb +6 -37
  46. data/lib/bridgetown-core/plugin_manager.rb +3 -2
  47. data/lib/bridgetown-core/rack/boot.rb +5 -0
  48. data/lib/bridgetown-core/rack/logger.rb +14 -4
  49. data/lib/bridgetown-core/rack/roda.rb +102 -8
  50. data/lib/bridgetown-core/rack/routes.rb +67 -2
  51. data/lib/bridgetown-core/resource/base.rb +4 -6
  52. data/lib/bridgetown-core/resource/destination.rb +18 -0
  53. data/lib/bridgetown-core/resource/permalink_processor.rb +6 -4
  54. data/lib/bridgetown-core/resource/relations.rb +1 -1
  55. data/lib/bridgetown-core/utils/aux.rb +2 -1
  56. data/lib/bridgetown-core/utils/require_gems.rb +3 -6
  57. data/lib/bridgetown-core/utils.rb +24 -11
  58. data/lib/bridgetown-core/version.rb +2 -2
  59. data/lib/bridgetown-core/watcher.rb +19 -6
  60. data/lib/bridgetown-core.rb +8 -2
  61. data/lib/site_template/README.md +2 -2
  62. data/lib/site_template/bridgetown.config.yml +3 -0
  63. data/lib/site_template/frontend/javascript/index.js.erb +1 -0
  64. data/lib/site_template/frontend/styles/syntax-highlighting.css +77 -0
  65. data/lib/site_template/package.json.erb +18 -17
  66. data/lib/site_template/server/roda_app.rb +3 -6
  67. data/lib/site_template/src/404.html +2 -1
  68. data/lib/site_template/src/500.html +10 -0
  69. metadata +20 -5
  70. data/lib/bridgetown-core/publisher.rb +0 -29
@@ -9,10 +9,12 @@ module Bridgetown
9
9
 
10
10
  def initialize(site)
11
11
  @site = site
12
+ @defaults_cache = {}
12
13
  end
13
14
 
14
15
  def reset
15
16
  @glob_cache = {}
17
+ @defaults_cache = {}
16
18
  end
17
19
 
18
20
  def ensure_time!(set)
@@ -29,16 +31,19 @@ module Bridgetown
29
31
  # Collects a hash with all default values for a resource
30
32
  #
31
33
  # @param path [String] the relative path of the resource
32
- # @param collection [Symbol] :posts, :pages, etc.
34
+ # @param collection_name [Symbol] :posts, :pages, etc.
33
35
  #
34
- # @returns [Hash] all default values (an empty hash if there are none)
35
- def all(path, collection)
36
- defaults = {}
36
+ # @return [Hash] all default values (an empty hash if there are none)
37
+ def all(path, collection_name)
38
+ if @defaults_cache.key?([path, collection_name])
39
+ return @defaults_cache[[path, collection_name]]
40
+ end
37
41
 
42
+ defaults = {}
38
43
  merge_data_cascade_for_path(path, defaults)
39
44
 
40
45
  old_scope = nil
41
- matching_sets(path, collection).each do |set|
46
+ matching_sets(path, collection_name).each do |set|
42
47
  if has_precedence?(old_scope, set["scope"])
43
48
  defaults = Utils.deep_merge_hashes(defaults, set["values"])
44
49
  old_scope = set["scope"]
@@ -46,7 +51,8 @@ module Bridgetown
46
51
  defaults = Utils.deep_merge_hashes(set["values"], defaults)
47
52
  end
48
53
  end
49
- defaults
54
+
55
+ @defaults_cache[[path, collection_name]] = defaults
50
56
  end
51
57
 
52
58
  private
@@ -123,7 +129,7 @@ module Bridgetown
123
129
  # @param scope [Hash] the defaults set being asked about
124
130
  # @param collection [Symbol] the collection of the resource being processed
125
131
  #
126
- # @returns [Boolean] whether either of the above conditions are satisfied
132
+ # @return [Boolean] whether either of the above conditions are satisfied
127
133
  def applies_collection?(scope, collection)
128
134
  !scope.key?("collection") || scope["collection"].eql?(collection.to_s)
129
135
  end
@@ -132,7 +138,7 @@ module Bridgetown
132
138
  #
133
139
  # @param set [Hash] the default value hash as defined in bridgetown.config.yml
134
140
  #
135
- # @returns [Boolean] if the set is valid and can be used
141
+ # @return [Boolean] if the set is valid and can be used
136
142
  def valid?(set)
137
143
  set.is_a?(Hash) && set["values"].is_a?(Hash)
138
144
  end
@@ -4,6 +4,7 @@ module Bridgetown
4
4
  class GeneratedPage
5
5
  include LayoutPlaceable
6
6
  include LiquidRenderable
7
+ include Localizable
7
8
  include Publishable
8
9
  include Transformable
9
10
 
@@ -0,0 +1,36 @@
1
+ # Frozen-string-literal: true
2
+
3
+ require "kramdown-parser-gfm"
4
+
5
+ module Kramdown
6
+ module Parser
7
+ class GFM
8
+ MARK_DELIMITER = %r{(==|::)+}.freeze
9
+ MARK_MATCH = %r{#{MARK_DELIMITER}(?!\s|=|:).*?[^\s=:]#{MARK_DELIMITER}}m.freeze
10
+
11
+ # Monkey-patch GFM initializer to add our new mark parser
12
+ alias_method :_old_initialize, :initialize
13
+ def initialize(source, options)
14
+ _old_initialize(source, options)
15
+ @span_parsers << :mark if @options[:mark_highlighting]
16
+ end
17
+
18
+ def parse_mark
19
+ line_number = @src.current_line_number
20
+
21
+ @src.pos += @src.matched_size
22
+ el = Element.new(:html_element, "mark", {}, category: :span, line: line_number)
23
+ @tree.children << el
24
+
25
+ env = save_env
26
+ reset_env(src: Kramdown::Utils::StringScanner.new(@src.matched[2..-3], line_number),
27
+ text_type: :text)
28
+ parse_spans(el)
29
+ restore_env(env)
30
+
31
+ el
32
+ end
33
+ define_parser(:mark, MARK_MATCH)
34
+ end
35
+ end
36
+ end
@@ -95,8 +95,7 @@ module Bridgetown
95
95
 
96
96
  # @return [Bridgetown::Resource::Base]
97
97
  def as_resource_in_collection
98
- collection.resources << to_resource.read!
99
- collection.resources.last
98
+ collection.add_resource_from_model(self)
100
99
  end
101
100
 
102
101
  # @return [Bridgetown::Resource::Base]
@@ -3,55 +3,24 @@
3
3
  module Bridgetown
4
4
  class Plugin
5
5
  extend ActiveSupport::DescendantsTracker
6
+ include Bridgetown::Prioritizable
6
7
 
7
- PRIORITIES = {
8
- low: -10,
8
+ self.priorities = {
9
9
  highest: 100,
10
- lowest: -100,
11
- normal: 0,
12
10
  high: 10,
11
+ normal: 0,
12
+ low: -10,
13
+ lowest: -100,
13
14
  }.freeze
14
15
 
15
16
  SourceManifest = Struct.new(:origin, :components, :content, :layouts, keyword_init: true)
16
17
 
17
- # Get or set the priority of this plugin. When called without an
18
- # argument it returns the priority. When an argument is given, it will
19
- # set the priority.
20
- #
21
- # priority - The Symbol priority (default: nil). Valid options are:
22
- # :lowest, :low, :normal, :high, :highest
23
- #
24
- # Returns the Symbol priority.
25
- def self.priority(priority = nil)
26
- @priority ||= nil
27
- @priority = priority if priority && PRIORITIES.key?(priority)
28
- @priority || :normal
29
- end
30
-
31
- # Spaceship is priority [higher -> lower]
32
- #
33
- # other - The class to be compared.
34
- #
35
- # Returns -1, 0, 1.
36
- def self.<=>(other)
37
- PRIORITIES[other.priority] <=> PRIORITIES[priority]
38
- end
39
-
40
- # Spaceship is priority [higher -> lower]
41
- #
42
- # other - The class to be compared.
43
- #
44
- # Returns -1, 0, 1.
45
- def <=>(other)
46
- self.class <=> other.class
47
- end
48
-
49
18
  # Initialize a new plugin. This should be overridden by the subclass.
50
19
  #
51
20
  # config - The Hash of configuration options.
52
21
  #
53
22
  # Returns a new instance.
54
- def initialize(config = {})
23
+ def initialize(config = {}) # rubocop:disable Style/RedundantInitialize
55
24
  # no-op for default
56
25
  end
57
26
  end
@@ -40,7 +40,8 @@ module Bridgetown
40
40
  @loaders_manager = Bridgetown::Utils::LoadersManager.new(site.config)
41
41
  end
42
42
 
43
- def self.require_from_bundler
43
+ def self.require_from_bundler(skip_yarn: false) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
44
+ # NOTE: investigate why this ENV var is really necessary
44
45
  if !ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
45
46
  require "bundler"
46
47
 
@@ -48,7 +49,7 @@ module Bridgetown
48
49
  (dep.groups & [PLUGINS_GROUP]).any? && dep.should_include?
49
50
  end
50
51
 
51
- install_yarn_dependencies(required_gems)
52
+ install_yarn_dependencies(required_gems) unless skip_yarn
52
53
 
53
54
  required_gems.each do |installed_gem|
54
55
  add_registered_plugin installed_gem
@@ -19,6 +19,11 @@ module Bridgetown
19
19
  attr_accessor :loaders_manager
20
20
  end
21
21
 
22
+ # Start up the Roda Rack application and the Zeitwerk autoloaders. Ensure the
23
+ # Roda app is provided the preloaded Bridgetown site configuration. Handle
24
+ # any uncaught Roda errors.
25
+ #
26
+ # @param [Bridgetown::Rack::Roda] optional, defaults to the `RodaApp` constant
22
27
  def self.boot(roda_app = nil)
23
28
  self.loaders_manager =
24
29
  Bridgetown::Utils::LoadersManager.new(Bridgetown::Current.preloaded_configuration)
@@ -4,19 +4,29 @@ require "logger"
4
4
 
5
5
  module Bridgetown
6
6
  module Rack
7
- class Logger < Logger
7
+ class Logger < Bridgetown::LogWriter
8
8
  def self.message_with_prefix(msg)
9
- return if msg.include?("/_bridgetown/live_reload")
9
+ # return if msg.include?("/_bridgetown/live_reload")
10
10
 
11
11
  "\e[35m[Server]\e[0m #{msg}"
12
12
  end
13
13
 
14
- def initialize(*)
15
- super
14
+ def enable_prefix
16
15
  @formatter = proc do |_, _, _, msg|
17
16
  self.class.message_with_prefix(msg)
18
17
  end
19
18
  end
19
+
20
+ def add(severity, message = nil, progname = nil)
21
+ return if progname&.include?("/_bridgetown/live_reload")
22
+
23
+ super
24
+ end
25
+
26
+ def initialize(*_args)
27
+ super()
28
+ enable_prefix
29
+ end
20
30
  end
21
31
  end
22
32
  end
@@ -2,6 +2,13 @@
2
2
 
3
3
  require "rack/indifferent"
4
4
 
5
+ begin
6
+ # If it's in the Gemfile's :bridgetown_plugins group it's already been required, but we'll try
7
+ # again just to be on the safe side:
8
+ require "bridgetown-routes"
9
+ rescue LoadError
10
+ end
11
+
5
12
  class Roda
6
13
  module RodaPlugins
7
14
  module BridgetownSSR
@@ -12,6 +19,34 @@ class Roda
12
19
  end
13
20
 
14
21
  register_plugin :bridgetown_ssr, BridgetownSSR
22
+
23
+ module BridgetownBoot
24
+ module InstanceMethods
25
+ # Helper shorthand for Bridgetown::Current.site
26
+ # @return [Bridgetown::Site]
27
+ def bridgetown_site
28
+ Bridgetown::Current.site
29
+ end
30
+ end
31
+
32
+ Roda::RodaRequest.alias_method :_previous_roda_cookies, :cookies
33
+
34
+ module RequestMethods
35
+ # Monkeypatch Roda/Rack's Request object so it returns a hash which allows for
36
+ # indifferent access
37
+ def cookies
38
+ # TODO: maybe replace with a simpler hash that offers an overloaded `[]` method
39
+ _previous_roda_cookies.with_indifferent_access
40
+ end
41
+
42
+ # Starts up the Bridgetown routing system
43
+ def bridgetown
44
+ Bridgetown::Rack::Routes.start!(scope)
45
+ end
46
+ end
47
+ end
48
+
49
+ register_plugin :bridgetown_boot, BridgetownBoot
15
50
  end
16
51
  end
17
52
 
@@ -24,6 +59,7 @@ module Bridgetown
24
59
  plugin :json_parser
25
60
  plugin :cookies
26
61
  plugin :streaming
62
+ plugin :bridgetown_boot
27
63
  plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
28
64
  plugin :not_found do
29
65
  output_folder = Bridgetown::Current.preloaded_configuration.destination
@@ -31,15 +67,76 @@ module Bridgetown
31
67
  rescue Errno::ENOENT
32
68
  "404 Not Found"
33
69
  end
70
+ plugin :exception_page
34
71
  plugin :error_handler do |e|
35
- puts "\n#{e.class} (#{e.message}):\n\n"
36
- puts e.backtrace
72
+ Bridgetown::Errors.print_build_error(
73
+ e, logger: Bridgetown::LogAdapter.new(self.class.opts[:common_logger])
74
+ )
75
+ next exception_page(e) if ENV.fetch("RACK_ENV", nil) == "development"
76
+
37
77
  output_folder = Bridgetown::Current.preloaded_configuration.destination
38
78
  File.read(File.join(output_folder, "500.html"))
39
79
  rescue Errno::ENOENT
40
80
  "500 Internal Server Error"
41
81
  end
42
82
 
83
+ ::Roda::RodaPlugins::ExceptionPage.class_eval do
84
+ def self.css
85
+ <<~CSS
86
+ html * { padding:0; margin:0; }
87
+ body * { padding:10px 20px; }
88
+ body * * { padding:0; }
89
+ body { font-family: -apple-system, sans-serif; font-size: 90%; }
90
+ body>div { border-bottom:1px solid #ddd; }
91
+ code { font-family: ui-monospace, monospace; }
92
+ h1 { font-weight: bold; margin-block-end: .8em; }
93
+ h2 { margin-block-end:.8em; }
94
+ h2 span { font-size:80%; color:#f7f7db; font-weight:normal; }
95
+ h3 { margin:1em 0 .5em 0; }
96
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
97
+ table {
98
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
99
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
100
+ thead th {
101
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
102
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
103
+ tbody th { text-align:right; opacity: 0.7; padding-right:.5em; }
104
+ table.vars { margin:5px 0 2px 40px; }
105
+ table.vars td, table.req td { font-family: ui-monospace, monospace; }
106
+ table td.code { width:100%;}
107
+ table td.code div { overflow:hidden; }
108
+ table.source th { color:#666; }
109
+ table.source td {
110
+ font-family: ui-monospace, monospace; white-space:pre; border-bottom:1px solid #eee; }
111
+ ul.traceback { list-style-type:none; }
112
+ ul.traceback li.frame { margin-bottom:1em; }
113
+ div.context { margin: 10px 0; }
114
+ div.context ol {
115
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
116
+ div.context ol li {
117
+ font-family: ui-monospace, monospace; white-space:pre; color:#666; cursor:pointer; }
118
+ div.context ol.context-line li { color:black; background-color:#f7f7db; }
119
+ div.context ol.context-line li span { float: right; }
120
+ div.commands { margin-left: 40px; }
121
+ div.commands a { color:black; text-decoration:none; }
122
+ #summary { background: #1D453C; color: white; }
123
+ #summary h2 { font-weight: normal; color: white; }
124
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
125
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
126
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
127
+ #summary a { color: #f47c3c; }
128
+ #explanation { background:#eee; }
129
+ #traceback { background: white; }
130
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
131
+ #summary table { border:none; background:transparent; }
132
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
133
+ #requestinfo h3 { margin-bottom:-1em; }
134
+ .error { background: #ffc; }
135
+ .specific { color:#cc3300; font-weight:bold; }
136
+ CSS
137
+ end
138
+ end
139
+
43
140
  before do
44
141
  if self.class.opts[:bridgetown_site]
45
142
  # The site had previously been initialized via the bridgetown_ssr plugin
@@ -51,14 +148,11 @@ module Bridgetown
51
148
  request.root do
52
149
  output_folder = Bridgetown::Current.preloaded_configuration.destination
53
150
  File.read(File.join(output_folder, "index.html"))
151
+ rescue StandardError
152
+ response.status = 500
153
+ "<p>ERROR: cannot find <code>index.html</code> in the output folder.</p>"
54
154
  end
55
155
  end
56
-
57
- # Helper shorthand for Bridgetown::Current.site
58
- # @return [Bridgetown::Site]
59
- def bridgetown_site
60
- Bridgetown::Current.site
61
- end
62
156
  end
63
157
  end
64
158
  end
@@ -9,19 +9,49 @@ module Bridgetown
9
9
  end
10
10
 
11
11
  class Routes
12
+ include Bridgetown::Prioritizable
13
+
14
+ self.priorities = {
15
+ highest: "010",
16
+ high: "020",
17
+ normal: "030",
18
+ low: "040",
19
+ lowest: "050",
20
+ }.freeze
21
+
12
22
  class << self
13
- attr_accessor :tracked_subclasses, :router_block
23
+ # @return [Hash<String, Class(Routes)>]
24
+ attr_accessor :tracked_subclasses
25
+
26
+ # @return [Proc]
27
+ attr_accessor :router_block
28
+
29
+ # Spaceship is priority [higher -> lower]
30
+ #
31
+ # @param other [Class(Routes)] The class to be compared.
32
+ # @return [Integer] -1, 0, 1.
33
+ def <=>(other)
34
+ "#{priorities[priority]}#{self}" <=> "#{priorities[other.priority]}#{other}"
35
+ end
14
36
 
37
+ # @param base [Class(Routes)]
15
38
  def inherited(base)
16
39
  Bridgetown::Rack::Routes.track_subclass base
17
40
  super
18
41
  end
19
42
 
43
+ # @param klass [Class(Routes)]
20
44
  def track_subclass(klass)
21
45
  Bridgetown::Rack::Routes.tracked_subclasses ||= {}
22
46
  Bridgetown::Rack::Routes.tracked_subclasses[klass.name] = klass
23
47
  end
24
48
 
49
+ # @return [Array<Class(Routes)>]
50
+ def sorted_subclasses
51
+ Bridgetown::Rack::Routes.tracked_subclasses&.values&.sort
52
+ end
53
+
54
+ # @return [void]
25
55
  def reload_subclasses
26
56
  Bridgetown::Rack::Routes.tracked_subclasses&.each_key do |klassname|
27
57
  Kernel.const_get(klassname)
@@ -30,16 +60,38 @@ module Bridgetown
30
60
  end
31
61
  end
32
62
 
63
+ # Add a router block via the current Routes class
64
+ #
65
+ # Example:
66
+ #
67
+ # class Routes::Hello < Bridgetown::Rack::Routes
68
+ # route do |r|
69
+ # r.get "hello", String do |name|
70
+ # { hello: "friend #{name}" }
71
+ # end
72
+ # end
73
+ # end
74
+ #
75
+ # @param block [Proc]
33
76
  def route(&block)
34
77
  self.router_block = block
35
78
  end
36
79
 
80
+ # Initialize a new Routes instance and execute the route as part of the
81
+ # Roda app request cycle
82
+ #
83
+ # @param roda_app [Bridgetown::Rack::Roda]
37
84
  def merge(roda_app)
38
85
  return unless router_block
39
86
 
40
87
  new(roda_app).handle_routes
41
88
  end
42
89
 
90
+ # Start the Roda app request cycle. There are two different code paths
91
+ # depending on if there's a site `base_path` configured
92
+ #
93
+ # @param roda_app [Bridgetown::Rack::Roda]
94
+ # @return [void]
43
95
  def start!(roda_app)
44
96
  if Bridgetown::Current.preloaded_configuration.base_path == "/"
45
97
  load_all_routes roda_app
@@ -56,6 +108,12 @@ module Bridgetown
56
108
  nil
57
109
  end
58
110
 
111
+ # Run the Roda public plugin first, set up live reload if allowed, then
112
+ # run through all the Routes blocks. If the file-based router plugin
113
+ # is available, kick off that request process next.
114
+ #
115
+ # @param roda_app [Bridgetown::Rack::Roda]
116
+ # @return [void]
59
117
  def load_all_routes(roda_app)
60
118
  roda_app.request.public
61
119
 
@@ -64,7 +122,7 @@ module Bridgetown
64
122
  setup_live_reload roda_app
65
123
  end
66
124
 
67
- Bridgetown::Rack::Routes.tracked_subclasses&.each_value do |klass|
125
+ Bridgetown::Rack::Routes.sorted_subclasses&.each do |klass|
68
126
  klass.merge roda_app
69
127
  end
70
128
 
@@ -73,6 +131,7 @@ module Bridgetown
73
131
  Bridgetown::Routes::RodaRouter.start!(roda_app)
74
132
  end
75
133
 
134
+ # @param app [Bridgetown::Rack::Roda]
76
135
  def setup_live_reload(app) # rubocop:disable Metrics/AbcSize
77
136
  sleep_interval = 0.2
78
137
  file_to_check = File.join(app.class.opts[:bridgetown_preloaded_config].destination,
@@ -102,14 +161,20 @@ module Bridgetown
102
161
  end
103
162
  end
104
163
 
164
+ # @param roda_app [Bridgetown::Rack::Roda]
105
165
  def initialize(roda_app)
106
166
  @_roda_app = roda_app
107
167
  end
108
168
 
169
+ # Execute the router block via the instance, passing it the Roda request
170
+ #
171
+ # @return [Object] whatever is returned by the router block as expected
172
+ # by the Roda API
109
173
  def handle_routes
110
174
  instance_exec(@_roda_app.request, &self.class.router_block)
111
175
  end
112
176
 
177
+ # Any missing methods are passed along to the underlying Roda app if possible
113
178
  def method_missing(method_name, *args, **kwargs, &block)
114
179
  if @_roda_app.respond_to?(method_name.to_sym)
115
180
  @_roda_app.send method_name.to_sym, *args, **kwargs, &block
@@ -7,6 +7,7 @@ module Bridgetown
7
7
  include Bridgetown::Publishable
8
8
  include Bridgetown::LayoutPlaceable
9
9
  include Bridgetown::LiquidRenderable
10
+ include Bridgetown::Localizable
10
11
 
11
12
  # @return [HashWithDotAccess::Hash]
12
13
  attr_reader :data
@@ -210,10 +211,7 @@ module Bridgetown
210
211
  def requires_destination?
211
212
  collection.write? && data.config&.output != false
212
213
  end
213
-
214
- def write?
215
- requires_destination? && site.publisher.publish?(self)
216
- end
214
+ alias_method :write?, :requires_destination?
217
215
 
218
216
  # Write the generated Document file to the destination directory.
219
217
  #
@@ -295,7 +293,7 @@ module Bridgetown
295
293
 
296
294
  private
297
295
 
298
- def ensure_default_data # rubocop:todo Metrics/AbcSize
296
+ def ensure_default_data
299
297
  determine_locale
300
298
 
301
299
  slug = if matches = relative_path.to_s.match(DATE_FILENAME_MATCHER) # rubocop:disable Lint/AssignmentInCondition
@@ -305,7 +303,7 @@ module Bridgetown
305
303
  basename_without_ext
306
304
  end
307
305
 
308
- slug.chomp!(".#{data.locale}") if data.locale && slug.ends_with?(".#{data.locale}")
306
+ Bridgetown::Utils.chomp_locale_suffix!(slug, data.locale)
309
307
 
310
308
  data.slug ||= slug
311
309
  data.title ||= Bridgetown::Utils.titleize_slug(slug)
@@ -12,6 +12,7 @@ module Bridgetown
12
12
  # @param resource [Bridgetown::Resource::Base]
13
13
  def initialize(resource)
14
14
  @resource = resource
15
+ warn_on_rails_style_extension
15
16
  @output_ext = resource.transformer.final_ext
16
17
  end
17
18
 
@@ -46,6 +47,23 @@ module Bridgetown
46
47
  Bridgetown.logger.debug "Writing:", path
47
48
  File.write(path, output, mode: "wb")
48
49
  end
50
+
51
+ private
52
+
53
+ def warn_on_rails_style_extension
54
+ return unless resource.relative_path.fnmatch?("*.{html,json,js}.*", File::FNM_EXTGLOB)
55
+
56
+ Bridgetown.logger.warn("Uh oh!", "You're using a Rails-style filename extension in:")
57
+ Bridgetown.logger.warn("", resource.relative_path)
58
+ Bridgetown.logger.warn(
59
+ "", "Instead, you can use either the desired output file extension or set a permalink."
60
+ )
61
+ Bridgetown.logger.warn(
62
+ "For more info:",
63
+ "https://www.bridgetownrb.com/docs/template-engines/erb-and-beyond#extensions-and-permalinks"
64
+ )
65
+ Bridgetown.logger.warn("")
66
+ end
49
67
  end
50
68
  end
51
69
  end
@@ -113,9 +113,8 @@ module Bridgetown
113
113
  if resource.site.config["collections_dir"].present?
114
114
  path.delete_prefix! "#{resource.site.config["collections_dir"]}/"
115
115
  end
116
- if resource.data.locale && path.ends_with?(".#{resource.data.locale}")
117
- path.chomp!(".#{resource.data.locale}")
118
- end
116
+
117
+ Bridgetown::Utils.chomp_locale_suffix!(path, resource.data.locale)
119
118
  end,
120
119
  }
121
120
  end
@@ -137,7 +136,10 @@ module Bridgetown
137
136
 
138
137
  # @param resource [Bridgetown::Resource::Base]
139
138
  register_placeholder :locale, ->(resource) do
140
- next nil if resource.data.locale&.to_sym == resource.site.config.default_locale
139
+ if !resource.site.config.prefix_default_locale &&
140
+ resource.data.locale&.to_sym == resource.site.config.default_locale
141
+ next nil
142
+ end
141
143
 
142
144
  locale_data = resource.data.locale&.to_sym
143
145
  resource.site.config.available_locales.include?(locale_data) ? locale_data.to_s : nil
@@ -68,7 +68,7 @@ module Bridgetown
68
68
  # @return [String]
69
69
  def kind_of_relation_for_type(type)
70
70
  relation_schema&.each do |relation_type, collections|
71
- collections = Array(collections).yield_self do |collections_arr|
71
+ collections = Array(collections).then do |collections_arr|
72
72
  collections_arr +
73
73
  collections_arr.map { |item| ActiveSupport::Inflector.pluralize(item) }
74
74
  end.flatten.uniq
@@ -20,7 +20,8 @@ module Bridgetown
20
20
  def self.run_process(name, color, cmd)
21
21
  Thread.new do
22
22
  rd, wr = IO.pipe("BINARY")
23
- pid = Process.spawn(cmd, out: wr, err: wr, pgroup: true)
23
+ pid = Process.spawn({ "BRIDGETOWN_NO_BUNDLER_REQUIRE" => nil },
24
+ cmd, out: wr, err: wr, pgroup: true)
24
25
  @mutex.synchronize do
25
26
  add_pid pid
26
27
  end
@@ -41,15 +41,12 @@ module Bridgetown
41
41
  require name
42
42
  rescue LoadError => e
43
43
  Bridgetown.logger.error "Dependency Error:", <<~MSG
44
- Yikes! It looks like you don't have #{name} or one of its dependencies installed.
45
- In order to use Bridgetown as currently configured, you'll need to install this gem.
44
+ Oops! It looks like you don't have #{name} or one of its dependencies installed.
45
+ Please double-check you've added #{name} to your Gemfile.
46
46
 
47
- If you've run Bridgetown with `bundle exec`, ensure that you have included the #{name}
48
- gem in your Gemfile as well.
47
+ If you're stuck, you can find help at https://www.bridgetownrb.com/community
49
48
 
50
49
  The full error message from Ruby is: '#{e.message}'
51
-
52
- If you run into trouble, you can find helpful resources at https://www.bridgetownrb.com/docs/community/
53
50
  MSG
54
51
  raise Bridgetown::Errors::MissingDependencyException, name
55
52
  end