bridgetown-core 1.0.0 → 1.1.0.beta3

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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -1
  3. data/bridgetown-core.gemspec +0 -1
  4. data/lib/bridgetown-core/collection.rb +39 -22
  5. data/lib/bridgetown-core/commands/apply.rb +3 -3
  6. data/lib/bridgetown-core/commands/build.rb +6 -6
  7. data/lib/bridgetown-core/commands/concerns/actions.rb +3 -2
  8. data/lib/bridgetown-core/commands/concerns/build_options.rb +2 -2
  9. data/lib/bridgetown-core/commands/configure.rb +1 -1
  10. data/lib/bridgetown-core/commands/console.rb +5 -5
  11. data/lib/bridgetown-core/commands/doctor.rb +7 -7
  12. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +95 -12
  13. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +1 -6
  14. data/lib/bridgetown-core/commands/new.rb +20 -19
  15. data/lib/bridgetown-core/commands/plugins.rb +47 -9
  16. data/lib/bridgetown-core/commands/registrations.rb +2 -3
  17. data/lib/bridgetown-core/commands/serve.rb +2 -2
  18. data/lib/bridgetown-core/commands/start.rb +3 -0
  19. data/lib/bridgetown-core/commands/webpack/update.rb +3 -3
  20. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +19 -14
  21. data/lib/bridgetown-core/component.rb +14 -8
  22. data/lib/bridgetown-core/concerns/localizable.rb +20 -0
  23. data/lib/bridgetown-core/concerns/prioritizable.rb +44 -0
  24. data/lib/bridgetown-core/concerns/publishable.rb +11 -1
  25. data/lib/bridgetown-core/concerns/site/configurable.rb +2 -10
  26. data/lib/bridgetown-core/concerns/site/localizable.rb +5 -1
  27. data/lib/bridgetown-core/concerns/site/ssr.rb +3 -3
  28. data/lib/bridgetown-core/concerns/site/writable.rb +28 -0
  29. data/lib/bridgetown-core/concerns/transformable.rb +2 -2
  30. data/lib/bridgetown-core/configuration.rb +4 -2
  31. data/lib/bridgetown-core/configurations/bt-postcss/postcss.config.js +5 -3
  32. data/lib/bridgetown-core/configurations/bt-postcss.rb +1 -1
  33. data/lib/bridgetown-core/configurations/lit/esbuild-plugins.js +21 -0
  34. data/lib/bridgetown-core/configurations/lit/happy-days.lit.js +26 -0
  35. data/lib/bridgetown-core/configurations/lit/lit-components-entry.js +1 -0
  36. data/lib/bridgetown-core/configurations/lit/lit-ssr.config.js +6 -0
  37. data/lib/bridgetown-core/configurations/lit.rb +95 -0
  38. data/lib/bridgetown-core/configurations/open-props/variables.css.erb +11 -0
  39. data/lib/bridgetown-core/configurations/open-props.rb +21 -0
  40. data/lib/bridgetown-core/configurations/ruby2js/hello_world.js.rb +9 -0
  41. data/lib/bridgetown-core/configurations/ruby2js.rb +67 -0
  42. data/lib/bridgetown-core/configurations/shoelace.rb +50 -0
  43. data/lib/bridgetown-core/configurations/tailwindcss.rb +16 -2
  44. data/lib/bridgetown-core/configurations/turbo/turbo_transitions.js +1 -1
  45. data/lib/bridgetown-core/converters/erb_templates.rb +7 -2
  46. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +1 -1
  47. data/lib/bridgetown-core/converters/serbea_templates.rb +1 -4
  48. data/lib/bridgetown-core/drops/generated_page_drop.rb +2 -1
  49. data/lib/bridgetown-core/drops/resource_drop.rb +2 -1
  50. data/lib/bridgetown-core/errors.rb +5 -5
  51. data/lib/bridgetown-core/filters/translation_filters.rb +11 -0
  52. data/lib/bridgetown-core/filters/url_filters.rb +37 -10
  53. data/lib/bridgetown-core/filters.rb +3 -0
  54. data/lib/bridgetown-core/frontmatter_defaults.rb +14 -8
  55. data/lib/bridgetown-core/generated_page.rb +1 -0
  56. data/lib/bridgetown-core/kramdown/parser/gfm.rb +36 -0
  57. data/lib/bridgetown-core/model/base.rb +3 -4
  58. data/lib/bridgetown-core/plugin.rb +6 -37
  59. data/lib/bridgetown-core/plugin_manager.rb +3 -2
  60. data/lib/bridgetown-core/rack/boot.rb +7 -2
  61. data/lib/bridgetown-core/rack/logger.rb +14 -4
  62. data/lib/bridgetown-core/rack/roda.rb +106 -9
  63. data/lib/bridgetown-core/rack/routes.rb +67 -2
  64. data/lib/bridgetown-core/resource/base.rb +9 -6
  65. data/lib/bridgetown-core/resource/destination.rb +18 -0
  66. data/lib/bridgetown-core/resource/permalink_processor.rb +6 -4
  67. data/lib/bridgetown-core/resource/relations.rb +1 -1
  68. data/lib/bridgetown-core/ruby_template_view.rb +3 -3
  69. data/lib/bridgetown-core/static_file.rb +1 -1
  70. data/lib/bridgetown-core/tags/highlight.rb +1 -1
  71. data/lib/bridgetown-core/tags/post_url.rb +1 -1
  72. data/lib/bridgetown-core/url.rb +1 -1
  73. data/lib/bridgetown-core/utils/aux.rb +2 -1
  74. data/lib/bridgetown-core/utils/require_gems.rb +3 -6
  75. data/lib/bridgetown-core/utils.rb +24 -11
  76. data/lib/bridgetown-core/version.rb +2 -2
  77. data/lib/bridgetown-core/watcher.rb +21 -8
  78. data/lib/bridgetown-core.rb +8 -2
  79. data/lib/site_template/Gemfile.erb +4 -0
  80. data/lib/site_template/README.md +2 -2
  81. data/lib/site_template/bridgetown.config.yml +3 -0
  82. data/lib/site_template/frontend/javascript/index.js.erb +1 -0
  83. data/lib/site_template/frontend/styles/syntax-highlighting.css +77 -0
  84. data/lib/site_template/package.json.erb +18 -18
  85. data/lib/site_template/server/roda_app.rb +3 -6
  86. data/lib/site_template/src/404.html +2 -1
  87. data/lib/site_template/src/500.html +10 -0
  88. metadata +20 -19
  89. data/lib/bridgetown-core/publisher.rb +0 -29
@@ -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)
@@ -28,8 +33,8 @@ module Bridgetown
28
33
  rescue Roda::RodaError => e
29
34
  if e.message.include?("sessions plugin :secret option")
30
35
  raise Bridgetown::Errors::InvalidConfigurationError,
31
- "The Roda sessions plugin can't find a valid secret. Run `bin/bridgetown secret'" \
32
- " and put the key in a ENV var you can use to configure the session in `roda_app.rb'"
36
+ "The Roda sessions plugin can't find a valid secret. Run `bin/bridgetown secret' " \
37
+ "and put the key in a ENV var you can use to configure the session in `roda_app.rb'"
33
38
  end
34
39
 
35
40
  raise e
@@ -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
@@ -1,29 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack/indifferent"
3
+ begin
4
+ # If it's in the Gemfile's :bridgetown_plugins group it's already been required, but we'll try
5
+ # again just to be on the safe side:
6
+ require "bridgetown-routes"
7
+ rescue LoadError
8
+ end
4
9
 
5
10
  class Roda
6
11
  module RodaPlugins
7
12
  module BridgetownSSR
13
+ module InstanceMethods
14
+ # Helper shorthand for Bridgetown::Current.site
15
+ # @return [Bridgetown::Site]
16
+ def bridgetown_site
17
+ Bridgetown::Current.site
18
+ end
19
+ end
20
+
8
21
  def self.configure(app, _opts = {}, &block)
22
+ app.include Bridgetown::Filters::URLFilters
9
23
  app.opts[:bridgetown_site] =
10
24
  Bridgetown::Site.start_ssr!(loaders_manager: Bridgetown::Rack.loaders_manager, &block)
11
25
  end
12
26
  end
13
27
 
14
28
  register_plugin :bridgetown_ssr, BridgetownSSR
29
+
30
+ module BridgetownBoot
31
+ Roda::RodaRequest.alias_method :_previous_roda_cookies, :cookies
32
+
33
+ module RequestMethods
34
+ # Monkeypatch Roda/Rack's Request object so it returns a hash which allows for
35
+ # indifferent access
36
+ def cookies
37
+ # TODO: maybe replace with a simpler hash that offers an overloaded `[]` method
38
+ _previous_roda_cookies.with_indifferent_access
39
+ end
40
+
41
+ # Starts up the Bridgetown routing system
42
+ def bridgetown
43
+ Bridgetown::Rack::Routes.start!(scope)
44
+ end
45
+ end
46
+ end
47
+
48
+ register_plugin :bridgetown_boot, BridgetownBoot
15
49
  end
16
50
  end
17
51
 
18
52
  module Bridgetown
19
53
  module Rack
20
54
  class Roda < ::Roda
55
+ SiteContext = Struct.new(:registers) # for use by Liquid-esque URL helpers
56
+
21
57
  plugin :hooks
22
58
  plugin :common_logger, Bridgetown::Rack::Logger.new($stdout), method: :info
23
59
  plugin :json
24
60
  plugin :json_parser
61
+ plugin :indifferent_params
25
62
  plugin :cookies
26
63
  plugin :streaming
64
+ plugin :bridgetown_boot
27
65
  plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
28
66
  plugin :not_found do
29
67
  output_folder = Bridgetown::Current.preloaded_configuration.destination
@@ -31,19 +69,81 @@ module Bridgetown
31
69
  rescue Errno::ENOENT
32
70
  "404 Not Found"
33
71
  end
72
+ plugin :exception_page
34
73
  plugin :error_handler do |e|
35
- puts "\n#{e.class} (#{e.message}):\n\n"
36
- puts e.backtrace
74
+ Bridgetown::Errors.print_build_error(
75
+ e, logger: Bridgetown::LogAdapter.new(self.class.opts[:common_logger])
76
+ )
77
+ next exception_page(e) if ENV.fetch("RACK_ENV", nil) == "development"
78
+
37
79
  output_folder = Bridgetown::Current.preloaded_configuration.destination
38
80
  File.read(File.join(output_folder, "500.html"))
39
81
  rescue Errno::ENOENT
40
82
  "500 Internal Server Error"
41
83
  end
42
84
 
85
+ ::Roda::RodaPlugins::ExceptionPage.class_eval do
86
+ def self.css
87
+ <<~CSS
88
+ html * { padding:0; margin:0; }
89
+ body * { padding:10px 20px; }
90
+ body * * { padding:0; }
91
+ body { font-family: -apple-system, sans-serif; font-size: 90%; }
92
+ body>div { border-bottom:1px solid #ddd; }
93
+ code { font-family: ui-monospace, monospace; }
94
+ h1 { font-weight: bold; margin-block-end: .8em; }
95
+ h2 { margin-block-end:.8em; }
96
+ h2 span { font-size:80%; color:#f7f7db; font-weight:normal; }
97
+ h3 { margin:1em 0 .5em 0; }
98
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
99
+ table {
100
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
101
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
102
+ thead th {
103
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
104
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
105
+ tbody th { text-align:right; opacity: 0.7; padding-right:.5em; }
106
+ table.vars { margin:5px 0 2px 40px; }
107
+ table.vars td, table.req td { font-family: ui-monospace, monospace; }
108
+ table td.code { width:100%;}
109
+ table td.code div { overflow:hidden; }
110
+ table.source th { color:#666; }
111
+ table.source td {
112
+ font-family: ui-monospace, monospace; white-space:pre; border-bottom:1px solid #eee; }
113
+ ul.traceback { list-style-type:none; }
114
+ ul.traceback li.frame { margin-bottom:1em; }
115
+ div.context { margin: 10px 0; }
116
+ div.context ol {
117
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
118
+ div.context ol li {
119
+ font-family: ui-monospace, monospace; white-space:pre; color:#666; cursor:pointer; }
120
+ div.context ol.context-line li { color:black; background-color:#f7f7db; }
121
+ div.context ol.context-line li span { float: right; }
122
+ div.commands { margin-left: 40px; }
123
+ div.commands a { color:black; text-decoration:none; }
124
+ #summary { background: #1D453C; color: white; }
125
+ #summary h2 { font-weight: normal; color: white; }
126
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
127
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
128
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
129
+ #summary a { color: #f47c3c; }
130
+ #explanation { background:#eee; }
131
+ #traceback { background: white; }
132
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
133
+ #summary table { border:none; background:transparent; }
134
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
135
+ #requestinfo h3 { margin-bottom:-1em; }
136
+ .error { background: #ffc; }
137
+ .specific { color:#cc3300; font-weight:bold; }
138
+ CSS
139
+ end
140
+ end
141
+
43
142
  before do
44
143
  if self.class.opts[:bridgetown_site]
45
144
  # The site had previously been initialized via the bridgetown_ssr plugin
46
145
  Bridgetown::Current.site ||= self.class.opts[:bridgetown_site]
146
+ @context ||= SiteContext.new({ site: self.class.opts[:bridgetown_site] })
47
147
  end
48
148
  Bridgetown::Current.preloaded_configuration ||=
49
149
  self.class.opts[:bridgetown_preloaded_config]
@@ -51,14 +151,11 @@ module Bridgetown
51
151
  request.root do
52
152
  output_folder = Bridgetown::Current.preloaded_configuration.destination
53
153
  File.read(File.join(output_folder, "index.html"))
154
+ rescue StandardError
155
+ response.status = 500
156
+ "<p>ERROR: cannot find <code>index.html</code> in the output folder.</p>"
54
157
  end
55
158
  end
56
-
57
- # Helper shorthand for Bridgetown::Current.site
58
- # @return [Bridgetown::Site]
59
- def bridgetown_site
60
- Bridgetown::Current.site
61
- end
62
159
  end
63
160
  end
64
161
  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
@@ -180,6 +181,11 @@ module Bridgetown
180
181
  model.origin.id
181
182
  end
182
183
 
184
+ # @return [String]
185
+ def output_ext
186
+ destination&.output_ext
187
+ end
188
+
183
189
  def date
184
190
  data["date"] ||= site.time
185
191
  end
@@ -210,10 +216,7 @@ module Bridgetown
210
216
  def requires_destination?
211
217
  collection.write? && data.config&.output != false
212
218
  end
213
-
214
- def write?
215
- requires_destination? && site.publisher.publish?(self)
216
- end
219
+ alias_method :write?, :requires_destination?
217
220
 
218
221
  # Write the generated Document file to the destination directory.
219
222
  #
@@ -295,7 +298,7 @@ module Bridgetown
295
298
 
296
299
  private
297
300
 
298
- def ensure_default_data # rubocop:todo Metrics/AbcSize
301
+ def ensure_default_data
299
302
  determine_locale
300
303
 
301
304
  slug = if matches = relative_path.to_s.match(DATE_FILENAME_MATCHER) # rubocop:disable Lint/AssignmentInCondition
@@ -305,7 +308,7 @@ module Bridgetown
305
308
  basename_without_ext
306
309
  end
307
310
 
308
- slug.chomp!(".#{data.locale}") if data.locale && slug.ends_with?(".#{data.locale}")
311
+ Bridgetown::Utils.chomp_locale_suffix!(slug, data.locale)
309
312
 
310
313
  data.slug ||= slug
311
314
  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
@@ -22,16 +22,16 @@ module Bridgetown
22
22
  @site = page.site
23
23
  end
24
24
 
25
- def partial(_partial_name, _options = {})
25
+ def partial(_partial_name = nil, **_options)
26
26
  raise "Must be implemented in a subclass"
27
27
  end
28
28
 
29
- def render(item, options = {}, &block)
29
+ def render(item, **options, &block)
30
30
  if item.respond_to?(:render_in)
31
31
  result = item.render_in(self, &block)
32
32
  result&.html_safe
33
33
  else
34
- partial(item, options, &block)&.html_safe
34
+ partial(item, **options, &block)&.html_safe
35
35
  end
36
36
  end
37
37
 
@@ -108,7 +108,7 @@ module Bridgetown
108
108
  self.class.mtimes[path] = mtime
109
109
 
110
110
  FileUtils.mkdir_p(File.dirname(dest_path))
111
- FileUtils.rm(dest_path) if File.exist?(dest_path)
111
+ FileUtils.rm_rf(dest_path)
112
112
  Bridgetown.logger.debug "Saving file:", dest_path
113
113
  copy_file(dest_path)
114
114
 
@@ -88,7 +88,7 @@ module Bridgetown
88
88
  "class=\"language-#{@lang.to_s.tr("+", "-")}\"",
89
89
  "data-lang=\"#{@lang}\"",
90
90
  ].join(" ")
91
- "<figure class=\"highlight\"><pre><code #{code_attributes}>"\
91
+ "<figure class=\"highlight\"><pre><code #{code_attributes}>" \
92
92
  "#{code.chomp}</code></pre></figure>"
93
93
  end
94
94
  end
@@ -80,7 +80,7 @@ module Bridgetown
80
80
  next unless @post.deprecated_equality document
81
81
 
82
82
  Bridgetown::Deprecator.deprecation_message(
83
- "A call to "\
83
+ "A call to " \
84
84
  "'{% post_url #{@post.name} %}' did not match " \
85
85
  "a post using the new matching method of checking name " \
86
86
  "(path-date-slug) equality. Please make sure that you " \
@@ -100,7 +100,7 @@ module Bridgetown
100
100
  winner = pool.find { |key| @placeholders.key?(key) }
101
101
  if winner.nil?
102
102
  raise NoMethodError,
103
- "The URL template doesn't have #{pool.join(" or ")} keys. "\
103
+ "The URL template doesn't have #{pool.join(" or ")} keys. " \
104
104
  "Check your permalink template!"
105
105
  end
106
106
 
@@ -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