bridgetown-core 1.0.0.alpha9 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/bin/bridgetown +6 -1
  4. data/bridgetown-core.gemspec +4 -3
  5. data/lib/bridgetown-core/collection.rb +6 -6
  6. data/lib/bridgetown-core/commands/base.rb +18 -18
  7. data/lib/bridgetown-core/commands/build.rb +1 -1
  8. data/lib/bridgetown-core/commands/clean.rb +2 -2
  9. data/lib/bridgetown-core/commands/console.rb +1 -0
  10. data/lib/bridgetown-core/commands/esbuild/esbuild.config.js +27 -0
  11. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +216 -0
  12. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +47 -0
  13. data/lib/bridgetown-core/commands/esbuild/setup.rb +4 -0
  14. data/lib/bridgetown-core/commands/esbuild/update.rb +4 -0
  15. data/lib/bridgetown-core/commands/esbuild.rb +83 -0
  16. data/lib/bridgetown-core/commands/new.rb +80 -10
  17. data/lib/bridgetown-core/commands/plugins.rb +1 -1
  18. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +1 -1
  19. data/lib/bridgetown-core/commands/webpack/update.rb +3 -3
  20. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +1 -1
  21. data/lib/bridgetown-core/commands/webpack.rb +3 -3
  22. data/lib/bridgetown-core/component.rb +9 -3
  23. data/lib/bridgetown-core/concerns/site/configurable.rb +6 -0
  24. data/lib/bridgetown-core/concerns/site/content.rb +4 -4
  25. data/lib/bridgetown-core/concerns/site/extensible.rb +8 -0
  26. data/lib/bridgetown-core/concerns/site/processable.rb +23 -4
  27. data/lib/bridgetown-core/concerns/site/ssr.rb +2 -17
  28. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  29. data/lib/bridgetown-core/configurations/purgecss.rb +2 -1
  30. data/lib/bridgetown-core/configurations/render/render.yaml.erb +3 -0
  31. data/lib/bridgetown-core/configurations/stimulus.rb +41 -12
  32. data/lib/bridgetown-core/configurations/tailwindcss/css_imports.css +5 -0
  33. data/lib/bridgetown-core/configurations/tailwindcss.rb +31 -2
  34. data/lib/bridgetown-core/configurations/turbo/turbo_transitions.js +48 -0
  35. data/lib/bridgetown-core/configurations/turbo.rb +15 -5
  36. data/lib/bridgetown-core/configurations/vercel/vercel.json +45 -0
  37. data/lib/bridgetown-core/configurations/vercel/vercel_url.rb +12 -0
  38. data/lib/bridgetown-core/configurations/vercel.rb +4 -0
  39. data/lib/bridgetown-core/converters/erb_templates.rb +7 -9
  40. data/lib/bridgetown-core/converters/ruby_templates.rb +1 -1
  41. data/lib/bridgetown-core/converters/serbea_templates.rb +5 -8
  42. data/lib/bridgetown-core/drops/drop.rb +1 -1
  43. data/lib/bridgetown-core/drops/resource_drop.rb +28 -5
  44. data/lib/bridgetown-core/errors.rb +21 -0
  45. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -3
  46. data/lib/bridgetown-core/helpers.rb +3 -2
  47. data/lib/bridgetown-core/hooks.rb +51 -20
  48. data/lib/bridgetown-core/model/base.rb +24 -1
  49. data/lib/bridgetown-core/model/origin.rb +4 -6
  50. data/lib/bridgetown-core/model/repo_origin.rb +48 -0
  51. data/lib/bridgetown-core/rack/boot.rb +5 -9
  52. data/lib/bridgetown-core/rack/roda.rb +4 -5
  53. data/lib/bridgetown-core/rack/routes.rb +44 -10
  54. data/lib/bridgetown-core/rack/static_indexes.rb +2 -0
  55. data/lib/bridgetown-core/resource/base.rb +3 -1
  56. data/lib/bridgetown-core/ruby_template_view.rb +11 -0
  57. data/lib/bridgetown-core/site.rb +5 -0
  58. data/lib/bridgetown-core/tags/{webpack_path.rb → asset_path.rb} +7 -9
  59. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -1
  60. data/lib/bridgetown-core/utils/loaders_manager.rb +17 -0
  61. data/lib/bridgetown-core/utils.rb +88 -30
  62. data/lib/bridgetown-core/version.rb +1 -1
  63. data/lib/bridgetown-core/watcher.rb +74 -70
  64. data/lib/bridgetown-core.rb +1 -0
  65. data/lib/site_template/Gemfile.erb +17 -11
  66. data/lib/site_template/README.md +2 -2
  67. data/lib/site_template/{Rakefile → Rakefile.erb} +15 -0
  68. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.erb +11 -0
  69. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.rb +5 -0
  70. data/lib/site_template/TEMPLATES/erb/_layouts/default.erb +15 -0
  71. data/lib/site_template/TEMPLATES/erb/_layouts/page.erb +7 -0
  72. data/lib/site_template/TEMPLATES/erb/_layouts/post.erb +7 -0
  73. data/lib/site_template/TEMPLATES/erb/_partials/_footer.erb +3 -0
  74. data/lib/site_template/TEMPLATES/erb/_partials/_head.erb +10 -0
  75. data/lib/site_template/{src → TEMPLATES/liquid}/_components/footer.liquid +0 -0
  76. data/lib/site_template/TEMPLATES/liquid/_components/head.liquid +10 -0
  77. data/lib/site_template/TEMPLATES/liquid/_components/navbar.liquid +11 -0
  78. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/default.liquid +2 -2
  79. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/page.liquid +0 -0
  80. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/post.liquid +0 -0
  81. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.rb +5 -0
  82. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.serb +11 -0
  83. data/lib/site_template/TEMPLATES/serbea/_layouts/default.serb +15 -0
  84. data/lib/site_template/TEMPLATES/serbea/_layouts/page.serb +7 -0
  85. data/lib/site_template/TEMPLATES/serbea/_layouts/post.serb +7 -0
  86. data/lib/site_template/TEMPLATES/serbea/_partials/_footer.serb +3 -0
  87. data/lib/site_template/TEMPLATES/serbea/_partials/_head.serb +10 -0
  88. data/lib/site_template/frontend/javascript/index.js.erb +7 -3
  89. data/lib/site_template/frontend/styles/index.css +71 -6
  90. data/lib/site_template/package.json.erb +24 -9
  91. data/lib/site_template/ruby-version.erb +1 -0
  92. data/lib/site_template/server/roda_app.rb +5 -3
  93. data/lib/site_template/server/routes/hello.rb.sample +1 -1
  94. data/lib/site_template/src/images/logo.svg +91 -0
  95. data/lib/site_template/src/index.md.erb +22 -0
  96. data/lib/site_template/src/posts.md.erb +28 -0
  97. metadata +70 -22
  98. data/lib/bridgetown-core/configurations/swup.rb +0 -37
  99. data/lib/site_template/frontend/styles/index.scss +0 -17
  100. data/lib/site_template/src/_components/head.liquid +0 -10
  101. data/lib/site_template/src/_components/navbar.liquid +0 -5
  102. data/lib/site_template/src/_layouts/home.liquid +0 -7
  103. data/lib/site_template/src/images/.keep +0 -1
  104. data/lib/site_template/src/index.md +0 -7
  105. data/lib/site_template/src/posts.md +0 -14
@@ -1,16 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- say "Installing Turbo...", :green
3
+ say_status :turbo, "Installing Turbo..."
4
4
 
5
5
  run("yarn add @hotwired/turbo")
6
6
 
7
- say 'Adding Turbo to "frontend/javascript/index.js"...', :magenta
7
+ say_status :turbo, 'Adding Turbo to "frontend/javascript/index.js"...', :magenta
8
8
 
9
9
  javascript_import do
10
10
  <<~JS
11
- import Turbo from "@hotwired/turbo"
11
+ import * as Turbo from "@hotwired/turbo"
12
+
13
+ // Uncomment the line below to add transition animations when Turbo navigates.
14
+ // We recommend adding <meta name="turbo-cache-control" content="no-preview" />
15
+ // to your HTML head if you turn on transitions. Use data-turbo-transition="false"
16
+ // on your <main> element for pages where you don't want any transition animation.
17
+ //
18
+ // import "./turbo_transitions.js"
12
19
  JS
13
20
  end
14
21
 
15
- say "Turbo successfully added", :green
16
- say 'For further reading, check out "https://turbo.hotwired.dev/"', :blue
22
+ copy_file in_templates_dir("turbo_transitions.js"), "frontend/javascript/turbo_transitions.js"
23
+
24
+ say_status :turbo, "Turbo successfully added!", :magenta
25
+ say_status :turbo, "Take a look in your index.js file for optional animation setup.", :blue
26
+ say_status :turbo, 'For further reading, check out "https://turbo.hotwired.dev/"', :blue
@@ -0,0 +1,45 @@
1
+ {
2
+ "cleanUrls": true,
3
+ "trailingSlash": false,
4
+ "redirects": [],
5
+ "headers": [
6
+ {
7
+ "source": "/(.*)",
8
+ "headers": [
9
+ {
10
+ "key": "X-Content-Type-Options",
11
+ "value": "nosniff"
12
+ },
13
+ {
14
+ "key": "X-Frame-Options",
15
+ "value": "DENY"
16
+ },
17
+ {
18
+ "key": "X-XSS-Protection",
19
+ "value": "1; mode=block"
20
+ },
21
+ {
22
+ "key": "Referrer-Policy",
23
+ "value": "strict-origin"
24
+ },
25
+ {
26
+ "key": "Permissions-Policy",
27
+ "value": "geolocation=(self), microphone=()"
28
+ }
29
+ ]
30
+ },
31
+ {
32
+ "source": "/feed.xml",
33
+ "headers": [
34
+ {
35
+ "key": "Content-Type",
36
+ "value": "application/rss+xml"
37
+ },
38
+ {
39
+ "key": "Cache-Control",
40
+ "value": "public, max-age=3600"
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Builders::VercelUrl < SiteBuilder
4
+ def build
5
+ hook :site, :pre_render do |site|
6
+ next unless ENV["VERCEL_URL"] && ENV["VERCEL_ENV"] != "production"
7
+
8
+ Bridgetown.logger.info("Subbing Vercel URL")
9
+ site.config.update(url: "https://#{ENV["VERCEL_URL"]}")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ copy_file in_templates_dir("vercel.json"), "vercel.json"
4
+ copy_file in_templates_dir("vercel_url.rb"), "plugins/builders/vercel_url.rb"
@@ -38,7 +38,7 @@ module Bridgetown
38
38
  return if text.empty?
39
39
 
40
40
  src << bufvar << ".safe_append='"
41
- src << text.gsub(%r{['\\]}, '\\\\\&') # rubocop:disable Style/StringLiterals
41
+ src << text.gsub(%r{['\\]}, '\\\\\&')
42
42
  src << "'.freeze;"
43
43
  end
44
44
 
@@ -83,16 +83,14 @@ module Bridgetown
83
83
  options.merge!(options[:locals]) if options[:locals]
84
84
  options[:content] = yield if block_given?
85
85
 
86
- partial_segments = partial_name.split("/")
87
- partial_segments.last.sub!(%r!^!, "_")
88
- partial_name = partial_segments.join("/")
89
-
90
- Tilt::ErubiTemplate.new(
91
- site.in_source_dir(site.config[:partials_dir], "#{partial_name}.erb"),
86
+ partial_path = _partial_path(partial_name, "erb")
87
+ tmpl = site.tmp_cache["partial-tmpl:#{partial_path}"] ||= Tilt::ErubiTemplate.new(
88
+ partial_path,
92
89
  outvar: "@_erbout",
93
90
  bufval: "Bridgetown::OutputBuffer.new",
94
91
  engine_class: ERBEngine
95
- ).render(self, options)
92
+ )
93
+ tmpl.render(self, options)
96
94
  end
97
95
  end
98
96
 
@@ -115,7 +113,7 @@ module Bridgetown
115
113
  erb_view = Bridgetown::ERBView.new(convertible)
116
114
 
117
115
  erb_renderer = Tilt::ErubiTemplate.new(
118
- convertible.relative_path,
116
+ convertible.path,
119
117
  line_start(convertible),
120
118
  outvar: "@_erbout",
121
119
  bufval: "Bridgetown::OutputBuffer.new",
@@ -9,7 +9,7 @@ module Bridgetown
9
9
  def convert(content, convertible)
10
10
  erb_view = Bridgetown::ERBView.new(convertible)
11
11
  erb_view.instance_eval(
12
- content, convertible.relative_path.to_s, line_start(convertible)
12
+ content, convertible.path.to_s, line_start(convertible)
13
13
  ).to_s
14
14
  end
15
15
  end
@@ -11,13 +11,10 @@ module Bridgetown
11
11
  options.merge!(options[:locals]) if options[:locals]
12
12
  options[:content] = capture(&block) if block
13
13
 
14
- partial_segments = partial_name.split("/")
15
- partial_segments.last.sub!(%r!^!, "_")
16
- partial_name = partial_segments.join("/")
17
-
18
- Tilt::SerbeaTemplate.new(
19
- site.in_source_dir(site.config[:partials_dir], "#{partial_name}.serb")
20
- ).render(self, options)
14
+ partial_path = _partial_path(partial_name, "serb")
15
+ tmpl = site.tmp_cache["partial-tmpl:#{partial_path}"] ||=
16
+ Tilt::SerbeaTemplate.new(partial_path)
17
+ tmpl.render(self, options)
21
18
  end
22
19
  end
23
20
 
@@ -39,7 +36,7 @@ module Bridgetown
39
36
 
40
37
  serb_view = Bridgetown::SerbeaView.new(convertible)
41
38
 
42
- serb_renderer = Tilt::SerbeaTemplate.new(convertible.relative_path) { content }
39
+ serb_renderer = Tilt::SerbeaTemplate.new(convertible.path) { content }
43
40
 
44
41
  if convertible.is_a?(Bridgetown::Layout)
45
42
  serb_renderer.render(serb_view) do
@@ -110,7 +110,7 @@ module Bridgetown
110
110
  # underlying data hashes and performs a set union to ensure a list
111
111
  # of unique keys for the Drop.
112
112
  #
113
- # Returns an Array of unique keys for content for the Drop.
113
+ # @return [Array<String>]
114
114
  def keys
115
115
  (content_methods |
116
116
  mutations.keys |
@@ -6,7 +6,7 @@ module Bridgetown
6
6
  extend Forwardable
7
7
 
8
8
  NESTED_OBJECT_FIELD_BLACKLIST = %w(
9
- content output excerpt next previous
9
+ content output excerpt next previous next_resource previous_resource
10
10
  ).freeze
11
11
 
12
12
  mutable false
@@ -44,13 +44,15 @@ module Bridgetown
44
44
  cmp
45
45
  end
46
46
 
47
- def previous
48
- @previous ||= @obj.previous_resource.to_liquid
47
+ def next_resource
48
+ @next ||= @obj.next_resource.to_liquid
49
49
  end
50
+ alias_method :next, :next_resource
50
51
 
51
- def next
52
- @next ||= @obj.next_resource.to_liquid
52
+ def previous_resource
53
+ @previous ||= @obj.previous_resource.to_liquid
53
54
  end
55
+ alias_method :previous, :previous_resource
54
56
 
55
57
  # Generate a Hash for use in generating JSON.
56
58
  # This is useful if fields need to be cleared before the JSON can generate.
@@ -79,6 +81,27 @@ module Bridgetown
79
81
  result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
80
82
  end
81
83
  end
84
+
85
+ # Generates a list of keys with user content as their values.
86
+ # This gathers up the Drop methods and keys of the mutations and
87
+ # underlying data hashes and performs a set union to ensure a list
88
+ # of unique keys for the Drop.
89
+ #
90
+ # @return [Array<String>]
91
+ def keys
92
+ keys_to_remove = %w[next_resource previous_resource]
93
+ (content_methods |
94
+ mutations.keys |
95
+ fallback_data.keys).flatten.reject do |key|
96
+ keys_to_remove.include?(key)
97
+ end
98
+ end
99
+
100
+ # Inspect the drop's keys and values through a JSON representation
101
+ # of its keys and values.
102
+ def inspect
103
+ JSON.pretty_generate hash_for_json
104
+ end
82
105
  end
83
106
  end
84
107
  end
@@ -14,5 +14,26 @@ module Bridgetown
14
14
  PostURLError = Class.new(FatalException)
15
15
  InvalidURLError = Class.new(FatalException)
16
16
  InvalidConfigurationError = Class.new(FatalException)
17
+
18
+ def self.print_build_error(exc, trace: false)
19
+ Bridgetown.logger.error "Exception raised:", exc.class.to_s.bold
20
+ Bridgetown.logger.error exc.message.reset_ansi
21
+
22
+ trace_args = ["-t", "--trace"]
23
+ print_trace_msg = true
24
+ traces = if trace || ARGV.find { |arg| trace_args.include?(arg) }
25
+ print_trace_msg = false
26
+ exc.backtrace
27
+ else
28
+ exc.backtrace[0..4]
29
+ end
30
+ traces.each_with_index do |backtrace_line, index|
31
+ Bridgetown.logger.error "#{index + 1}:", backtrace_line.reset_ansi
32
+ end
33
+
34
+ return unless print_trace_msg
35
+
36
+ Bridgetown.logger.warn "Backtrace:", "Use the --trace option for complete information."
37
+ end
17
38
  end
18
39
  end
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Handles Generated Pages
4
- Bridgetown::Hooks.register :generated_pages, :post_init, reloadable: false do |page|
4
+ Bridgetown::Hooks.register_one :generated_pages, :post_init, reloadable: false do |page|
5
5
  if page.class != Bridgetown::PrototypePage && page.data["prototype"].is_a?(Hash)
6
6
  Bridgetown::PrototypeGenerator.add_matching_template(page)
7
7
  end
8
8
  end
9
9
 
10
10
  # Handles Resources
11
- Bridgetown::Hooks.register :resources, :post_read, reloadable: false do |resource|
11
+ Bridgetown::Hooks.register_one :resources, :post_read, reloadable: false do |resource|
12
12
  if resource.data["prototype"].is_a?(Hash)
13
13
  Bridgetown::PrototypeGenerator.add_matching_template(resource)
14
14
  end
15
15
  end
16
16
 
17
17
  # Ensure sites clear out templates before rebuild
18
- Bridgetown::Hooks.register :site, :after_reset, reloadable: false do |_site|
18
+ Bridgetown::Hooks.register_one :site, :after_reset, reloadable: false do |_site|
19
19
  Bridgetown::PrototypeGenerator.matching_templates.clear
20
20
  end
21
21
 
@@ -22,9 +22,10 @@ module Bridgetown
22
22
  @context = Context.new({ site: site })
23
23
  end
24
24
 
25
- def webpack_path(asset_type)
26
- Bridgetown::Utils.parse_webpack_manifest_file(site, asset_type.to_s)
25
+ def asset_path(asset_type)
26
+ Bridgetown::Utils.parse_frontend_manifest_file(site, asset_type.to_s)
27
27
  end
28
+ alias_method :webpack_path, :asset_path
28
29
 
29
30
  def live_reload_dev_js
30
31
  Bridgetown::Utils.live_reload_js(site)
@@ -17,34 +17,59 @@ module Bridgetown
17
17
 
18
18
  DEFAULT_PRIORITY = 20
19
19
 
20
- # compatibility layer for octopress-hooks users
21
20
  PRIORITY_MAP = {
22
21
  low: 10,
23
22
  normal: 20,
24
23
  high: 30,
25
24
  }.freeze
26
25
 
27
- # initial empty hooks
28
26
  @registry = {}
29
27
 
30
28
  NotAvailable = Class.new(RuntimeError)
31
29
  Uncallable = Class.new(RuntimeError)
32
30
 
33
- # Ensure the priority is a Fixnum
34
31
  def self.priority_value(priority)
35
32
  return priority if priority.is_a?(Integer)
36
33
 
37
34
  PRIORITY_MAP[priority] || DEFAULT_PRIORITY
38
35
  end
39
36
 
40
- # register hook(s) to be called later
37
+ # Sort registered hooks according to priority and load order
38
+ #
39
+ # @param hooks [Array<HookRegistration>]
40
+ def self.prioritized_hooks(hooks)
41
+ grouped_hooks = hooks.group_by(&:priority)
42
+ grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
43
+ end
44
+
45
+ # Register one or more hooks which may be triggered later for a particular event
46
+ #
47
+ # @param owners [Symbol, Array<Symbol>] name of the owner (`:site`, `:resource`, etc.)
48
+ # @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
49
+ # @param priority [Integer, Symbol] either `:low`, `:normal`, or `:high`, or an integer.
50
+ # Default is normal (20)
51
+ # @param reloadable [Boolean] whether the hook should be removed prior to a site reload.
52
+ # Default is true.
53
+ # @yield the block will be called when the event is triggered. Typically it receives at
54
+ # least one argument.
55
+ # @yieldparam obj the object which triggered the event hook
41
56
  def self.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
42
57
  Array(owners).each do |owner|
43
58
  register_one(owner, event, priority: priority, reloadable: reloadable, &block)
44
59
  end
45
60
  end
46
61
 
47
- # register a single hook to be called later
62
+ # Register a hook which may be triggered later for a particular event
63
+ #
64
+ # @param owner [Symbol] name of the owner (`:site`, `:resource`, etc.)
65
+ # @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
66
+ # @param priority [Integer, Symbol] either `:low`, `:normal`, or `:high`, or an integer.
67
+ # Default is normal (20)
68
+ # @param reloadable [Boolean] whether the hook should be removed prior to a site reload.
69
+ # Default is true.
70
+ # @yield the block will be called when the event is triggered. Typically it receives at
71
+ # least one argument.
72
+ # @yieldparam obj the object which triggered the event hook
48
73
  def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
49
74
  @registry[owner] ||= []
50
75
 
@@ -68,10 +93,28 @@ module Bridgetown
68
93
  block
69
94
  end
70
95
 
71
- def self.remove_hook(owner, _event, block)
96
+ # Delete a previously-registered hook
97
+ #
98
+ # @param owners [Symbol] name of the owner (`:site`, `:resource`, etc.)
99
+ # @param block [Proc] the exact block used originally to register the hook
100
+ def self.remove_hook(owner, block)
72
101
  @registry[owner].delete_if { |item| item.block == block }
73
102
  end
74
103
 
104
+ # Clear all hooks marked as reloadable from the registry
105
+ def self.clear_reloadable_hooks
106
+ Bridgetown.logger.debug("Clearing reloadable hooks")
107
+
108
+ @registry.each_value do |hooks|
109
+ hooks.delete_if(&:reloadable)
110
+ end
111
+ end
112
+
113
+ # Trigger all registered hooks for a particular owner and event.
114
+ # Any arguments after the initial two will be directly passed along to the hooks.
115
+ #
116
+ # @param owner [Symbol] name of the owner (`:site`, `:resource`, etc.)
117
+ # @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
75
118
  def self.trigger(owner, event, *args) # rubocop:disable Metrics/CyclomaticComplexity
76
119
  # proceed only if there are hooks to call
77
120
  hooks = @registry[owner]&.select { |item| item.event == event }
@@ -79,25 +122,13 @@ module Bridgetown
79
122
 
80
123
  prioritized_hooks(hooks).each do |hook|
81
124
  if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
82
- hook_info = args[0].respond_to?(:url) ? args[0].relative_path : hook.block
125
+ hook_info = args[0].respond_to?(:relative_path) ? args[0].relative_path : hook.block
83
126
  Bridgetown.logger.debug("Triggering hook:", "#{owner}:#{event} for #{hook_info}")
84
127
  end
85
128
  hook.block.call(*args)
86
129
  end
87
- end
88
-
89
- def self.prioritized_hooks(hooks)
90
- # sort hooks according to priority and load order
91
- grouped_hooks = hooks.group_by(&:priority)
92
- grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
93
- end
94
130
 
95
- def self.clear_reloadable_hooks
96
- Bridgetown.logger.debug("Clearing reloadable hooks")
97
-
98
- @registry.each_value do |hooks|
99
- hooks.delete_if(&:reloadable)
100
- end
131
+ true
101
132
  end
102
133
  end
103
134
  end
@@ -69,8 +69,23 @@ module Bridgetown
69
69
  attributes[:_origin_]
70
70
  end
71
71
 
72
+ def origin=(new_origin)
73
+ attributes[:_id_] = new_origin.id
74
+ attributes[:_origin_] = new_origin
75
+ end
76
+
72
77
  def persisted?
73
- id && origin.exists?
78
+ (id && origin&.exists?) == true
79
+ end
80
+
81
+ def save
82
+ unless origin.respond_to?(:write)
83
+ raise "`#{origin.class}' doesn't allow writing of model objects"
84
+ end
85
+
86
+ run_callbacks :save do
87
+ origin.write(self)
88
+ end
74
89
  end
75
90
 
76
91
  # @return [Bridgetown::Resource::Base]
@@ -100,11 +115,19 @@ module Bridgetown
100
115
  attributes[:_collection_]
101
116
  end
102
117
 
118
+ def collection=(new_collection)
119
+ attributes[:_collection_] = new_collection
120
+ end
121
+
103
122
  # @return [String]
104
123
  def content
105
124
  attributes[:_content_]
106
125
  end
107
126
 
127
+ def content=(new_content)
128
+ attributes[:_content_] = new_content
129
+ end
130
+
108
131
  def attributes
109
132
  @attributes ||= HashWithDotAccess::Hash.new
110
133
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # See bottom of file for specific origin requires...
4
-
5
3
  module Bridgetown
6
4
  module Model
7
5
  # Abstract Superclass
8
6
  class Origin
9
7
  extend ActiveSupport::DescendantsTracker
10
8
 
9
+ EAGER_LOAD_DESCENDANTS = %i(BuilderOrigin RepoOrigin PluginOrigin).freeze
10
+
11
11
  # @return [String]
12
12
  attr_accessor :id
13
13
 
@@ -42,9 +42,7 @@ module Bridgetown
42
42
  raise "Implement #exists? in a subclass of Bridgetown::Model::Origin"
43
43
  end
44
44
  end
45
+
46
+ Origin::EAGER_LOAD_DESCENDANTS.each { const_get _1 }
45
47
  end
46
48
  end
47
-
48
- require "bridgetown-core/model/builder_origin"
49
- require "bridgetown-core/model/repo_origin"
50
- require "bridgetown-core/model/plugin_origin"
@@ -25,6 +25,18 @@ module Bridgetown
25
25
  def data_file_extensions
26
26
  %w(.yaml .yml .json .csv .tsv .rb).freeze
27
27
  end
28
+
29
+ # Initializes a new repo object using a collection and a relative source path.
30
+ # You'll need to use this when you want to create and save a model to the source.
31
+ #
32
+ # @param collection [Bridgetown::Collection, String, Symbol] either a collection
33
+ # label or Collection object
34
+ # @param relative_path [Pathname, String] the source path of the file to save
35
+ def new_with_collection_path(collection, relative_path)
36
+ collection = collection.label if collection.is_a?(Bridgetown::Collection)
37
+
38
+ new("repo://#{collection}.collection/#{relative_path}")
39
+ end
28
40
  end
29
41
 
30
42
  def read
@@ -46,6 +58,23 @@ module Bridgetown
46
58
  @data
47
59
  end
48
60
 
61
+ def write(model)
62
+ if File.exist?(original_path) && !Bridgetown::Utils.has_yaml_header?(original_path)
63
+ raise Bridgetown::Errors::InvalidYAMLFrontMatterError,
64
+ "Only existing files containing YAML front matter can be overwritten by the model"
65
+ end
66
+
67
+ contents = "#{front_matter_to_yaml(model)}---\n\n#{model.content}"
68
+
69
+ # Create folders if necessary
70
+ dir = File.dirname(original_path)
71
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
72
+
73
+ File.write(original_path, contents)
74
+
75
+ true
76
+ end
77
+
49
78
  def url
50
79
  @url ||= URI.parse(id)
51
80
  end
@@ -121,6 +150,25 @@ module Bridgetown
121
150
  raise error
122
151
  end
123
152
  end
153
+
154
+ def front_matter_to_yaml(model)
155
+ data = model.data_attributes.to_h
156
+ data = data.deep_merge(data) do |_, _, v|
157
+ case v
158
+ when DateTime
159
+ v.to_time
160
+ when Symbol
161
+ v.to_s
162
+ else
163
+ v
164
+ end
165
+ end
166
+ data.each do |k, v|
167
+ data.delete(k) if v.nil?
168
+ end
169
+
170
+ data.to_yaml
171
+ end
124
172
  end
125
173
  end
126
174
  end
@@ -51,15 +51,11 @@ module Bridgetown
51
51
  loader.do_not_eager_load(File.join(server_folder, "roda_app.rb"))
52
52
 
53
53
  unless ENV["BRIDGETOWN_ENV"] == "production"
54
- begin
55
- Listen.to(server_folder) do |_modified, _added, _removed|
56
- loader.reload
57
- loader.eager_load
58
- Bridgetown::Rack::Routes.reload_subclasses
59
- end.start
60
- # interrupt isn't handled well by the listener
61
- rescue ThreadError # rubocop:disable Lint/SuppressedException
62
- end
54
+ Listen.to(server_folder) do |_modified, _added, _removed|
55
+ loader.reload
56
+ loader.eager_load
57
+ Bridgetown::Rack::Routes.reload_subclasses
58
+ end.start
63
59
  end
64
60
  end
65
61
 
@@ -23,6 +23,7 @@ module Bridgetown
23
23
  plugin :json
24
24
  plugin :json_parser
25
25
  plugin :cookies
26
+ plugin :streaming
26
27
  plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
27
28
  plugin :not_found do
28
29
  output_folder = Bridgetown::Current.preloaded_configuration.destination
@@ -39,7 +40,7 @@ module Bridgetown
39
40
  "500 Internal Server Error"
40
41
  end
41
42
 
42
- def _roda_run_main_route(r) # rubocop:disable Naming/MethodParameterName
43
+ before do
43
44
  if self.class.opts[:bridgetown_site]
44
45
  # The site had previously been initialized via the bridgetown_ssr plugin
45
46
  Bridgetown::Current.site ||= self.class.opts[:bridgetown_site]
@@ -47,14 +48,12 @@ module Bridgetown
47
48
  Bridgetown::Current.preloaded_configuration ||=
48
49
  self.class.opts[:bridgetown_preloaded_config]
49
50
 
50
- r.public
51
+ request.public
51
52
 
52
- r.root do
53
+ request.root do
53
54
  output_folder = Bridgetown::Current.preloaded_configuration.destination
54
55
  File.read(File.join(output_folder, "index.html"))
55
56
  end
56
-
57
- super
58
57
  end
59
58
 
60
59
  # Helper shorthand for Bridgetown::Current.site