bridgetown-core 1.0.0 → 1.1.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
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