bridgetown-core 1.0.0.alpha8 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/bin/bridgetown +14 -2
  4. data/bridgetown-core.gemspec +5 -4
  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/concerns/configuration_overridable.rb +7 -0
  10. data/lib/bridgetown-core/commands/console.rb +39 -12
  11. data/lib/bridgetown-core/commands/doctor.rb +8 -5
  12. data/lib/bridgetown-core/commands/esbuild/esbuild.config.js +27 -0
  13. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +216 -0
  14. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +47 -0
  15. data/lib/bridgetown-core/commands/esbuild/setup.rb +4 -0
  16. data/lib/bridgetown-core/commands/esbuild/update.rb +4 -0
  17. data/lib/bridgetown-core/commands/esbuild.rb +83 -0
  18. data/lib/bridgetown-core/commands/new.rb +80 -10
  19. data/lib/bridgetown-core/commands/plugins.rb +1 -1
  20. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +1 -1
  21. data/lib/bridgetown-core/commands/webpack/update.rb +3 -3
  22. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +1 -1
  23. data/lib/bridgetown-core/commands/webpack.rb +3 -3
  24. data/lib/bridgetown-core/component.rb +9 -3
  25. data/lib/bridgetown-core/concerns/site/configurable.rb +4 -0
  26. data/lib/bridgetown-core/concerns/site/content.rb +4 -4
  27. data/lib/bridgetown-core/concerns/site/extensible.rb +8 -0
  28. data/lib/bridgetown-core/concerns/site/processable.rb +22 -4
  29. data/lib/bridgetown-core/concerns/site/ssr.rb +2 -17
  30. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  31. data/lib/bridgetown-core/configurations/purgecss.rb +2 -1
  32. data/lib/bridgetown-core/configurations/render/render.yaml.erb +3 -0
  33. data/lib/bridgetown-core/configurations/stimulus.rb +40 -12
  34. data/lib/bridgetown-core/configurations/tailwindcss/css_imports.css +5 -0
  35. data/lib/bridgetown-core/configurations/tailwindcss.rb +31 -2
  36. data/lib/bridgetown-core/configurations/turbo/turbo_transitions.js +48 -0
  37. data/lib/bridgetown-core/configurations/turbo.rb +15 -5
  38. data/lib/bridgetown-core/converters/erb_templates.rb +2 -2
  39. data/lib/bridgetown-core/converters/ruby_templates.rb +1 -1
  40. data/lib/bridgetown-core/converters/serbea_templates.rb +71 -0
  41. data/lib/bridgetown-core/drops/drop.rb +1 -1
  42. data/lib/bridgetown-core/drops/resource_drop.rb +28 -5
  43. data/lib/bridgetown-core/errors.rb +21 -0
  44. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -3
  45. data/lib/bridgetown-core/helpers.rb +3 -2
  46. data/lib/bridgetown-core/hooks.rb +51 -20
  47. data/lib/bridgetown-core/liquid_renderer/file_system.rb +1 -3
  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 +2 -0
  56. data/lib/bridgetown-core/ruby_template_view.rb +4 -0
  57. data/lib/bridgetown-core/tags/{webpack_path.rb → asset_path.rb} +7 -9
  58. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -1
  59. data/lib/bridgetown-core/utils/loaders_manager.rb +11 -0
  60. data/lib/bridgetown-core/utils.rb +88 -30
  61. data/lib/bridgetown-core/version.rb +1 -1
  62. data/lib/bridgetown-core/watcher.rb +74 -70
  63. data/lib/bridgetown-core.rb +1 -1
  64. data/lib/site_template/Gemfile.erb +17 -11
  65. data/lib/site_template/README.md +2 -2
  66. data/lib/site_template/{Rakefile → Rakefile.erb} +15 -0
  67. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.erb +11 -0
  68. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.rb +5 -0
  69. data/lib/site_template/TEMPLATES/erb/_layouts/default.erb +15 -0
  70. data/lib/site_template/TEMPLATES/erb/_layouts/page.erb +7 -0
  71. data/lib/site_template/TEMPLATES/erb/_layouts/post.erb +7 -0
  72. data/lib/site_template/TEMPLATES/erb/_partials/_footer.erb +3 -0
  73. data/lib/site_template/TEMPLATES/erb/_partials/_head.erb +10 -0
  74. data/lib/site_template/{src → TEMPLATES/liquid}/_components/footer.liquid +0 -0
  75. data/lib/site_template/TEMPLATES/liquid/_components/head.liquid +10 -0
  76. data/lib/site_template/TEMPLATES/liquid/_components/navbar.liquid +11 -0
  77. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/default.liquid +2 -2
  78. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/page.liquid +0 -0
  79. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/post.liquid +0 -0
  80. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.rb +5 -0
  81. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.serb +11 -0
  82. data/lib/site_template/TEMPLATES/serbea/_layouts/default.serb +15 -0
  83. data/lib/site_template/TEMPLATES/serbea/_layouts/page.serb +7 -0
  84. data/lib/site_template/TEMPLATES/serbea/_layouts/post.serb +7 -0
  85. data/lib/site_template/TEMPLATES/serbea/_partials/_footer.serb +3 -0
  86. data/lib/site_template/TEMPLATES/serbea/_partials/_head.serb +10 -0
  87. data/lib/site_template/frontend/javascript/index.js.erb +7 -3
  88. data/lib/site_template/frontend/styles/index.css +71 -6
  89. data/lib/site_template/package.json.erb +24 -9
  90. data/lib/site_template/ruby-version.erb +1 -0
  91. data/lib/site_template/server/roda_app.rb +5 -3
  92. data/lib/site_template/server/routes/hello.rb.sample +1 -1
  93. data/lib/site_template/src/images/logo.svg +91 -0
  94. data/lib/site_template/src/index.md.erb +22 -0
  95. data/lib/site_template/src/posts.md.erb +28 -0
  96. metadata +82 -36
  97. data/lib/bridgetown-core/configurations/swup.rb +0 -37
  98. data/lib/site_template/frontend/styles/index.scss +0 -17
  99. data/lib/site_template/src/_components/head.liquid +0 -10
  100. data/lib/site_template/src/_components/navbar.liquid +0 -5
  101. data/lib/site_template/src/_layouts/home.liquid +0 -7
  102. data/lib/site_template/src/images/.keep +0 -1
  103. data/lib/site_template/src/index.md +0 -7
  104. data/lib/site_template/src/posts.md +0 -14
@@ -0,0 +1,48 @@
1
+ document.addEventListener("turbo:visit", () => {
2
+ let main = document.querySelector("main");
3
+ if (main.dataset.turboTransition == "false") return;
4
+
5
+ let [movement, scale] = ["15px", "0.99"];
6
+
7
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
8
+ [movement, scale] = ["7px", "1"]
9
+ };
10
+
11
+ main.style.transformOrigin = "50% 0%";
12
+ main.dataset.animatingOut = true;
13
+
14
+ main.animate(
15
+ [
16
+ { opacity: 1, transform: "translateY(0px) scale(1)" },
17
+ { opacity: 0, transform: `translateY(${movement}) scale(${scale})` }
18
+ ],
19
+ { duration: 300, easing: "cubic-bezier(0.45, 0, 0.55, 1)" }
20
+ );
21
+
22
+ Promise.all(main.getAnimations().map(animation => animation.finished)).then(() => {
23
+ if (main.dataset.animatingOut) main.style.visibility = "hidden"
24
+ })
25
+ });
26
+
27
+ document.addEventListener("turbo:load", () => {
28
+ let main = document.querySelector("main");
29
+ if (main.dataset.turboTransition == "false") return;
30
+
31
+ let [movement, scale] = ["-10px", "0.99"];
32
+
33
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
34
+ [movement, scale] = ["-5px", "1"]
35
+ };
36
+
37
+ main.style.visibility = "visible";
38
+ main.style.transformOrigin = "50% 0%";
39
+ delete main.dataset.animatingOut;
40
+
41
+ main.animate(
42
+ [
43
+ { opacity: 0, transform: `translateY(${movement}) scale(${scale})` },
44
+ { opacity: 1, transform: "translateY(0px) scale(1)" }
45
+ ],
46
+ { duration: 150, easing: "cubic-bezier(0.45, 0, 0.55, 1)" }
47
+ )
48
+ })
@@ -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
@@ -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
 
@@ -115,7 +115,7 @@ module Bridgetown
115
115
  erb_view = Bridgetown::ERBView.new(convertible)
116
116
 
117
117
  erb_renderer = Tilt::ErubiTemplate.new(
118
- convertible.relative_path,
118
+ convertible.path,
119
119
  line_start(convertible),
120
120
  outvar: "@_erbout",
121
121
  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
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "serbea"
4
+ require "rouge/lexers/serbea"
5
+
6
+ module Bridgetown
7
+ class SerbeaView < ERBView
8
+ include Serbea::Helpers
9
+
10
+ def partial(partial_name, options = {}, &block)
11
+ options.merge!(options[:locals]) if options[:locals]
12
+ options[:content] = capture(&block) if block
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)
21
+ end
22
+ end
23
+
24
+ module Converters
25
+ class SerbeaTemplates < Converter
26
+ priority :highest
27
+ input :serb
28
+
29
+ # Logic to do the Serbea content conversion.
30
+ #
31
+ # @param content [String] Content of the file (without front matter).
32
+ # @param convertible [
33
+ # Bridgetown::GeneratedPage, Bridgetown::Resource::Base, Bridgetown::Layout]
34
+ # The instantiated object which is processing the file.
35
+ #
36
+ # @return [String] The converted content.
37
+ def convert(content, convertible)
38
+ return content if convertible.data[:template_engine].to_s != "serbea"
39
+
40
+ serb_view = Bridgetown::SerbeaView.new(convertible)
41
+
42
+ serb_renderer = Tilt::SerbeaTemplate.new(convertible.path) { content }
43
+
44
+ if convertible.is_a?(Bridgetown::Layout)
45
+ serb_renderer.render(serb_view) do
46
+ convertible.current_document_output
47
+ end
48
+ else
49
+ serb_renderer.render(serb_view)
50
+ end
51
+ end
52
+
53
+ def matches(ext, convertible)
54
+ if convertible.data[:template_engine].to_s == "serbea" ||
55
+ (convertible.data[:template_engine].nil? &&
56
+ @config[:template_engine].to_s == "serbea")
57
+ convertible.data[:template_engine] = "serbea"
58
+ return true
59
+ end
60
+
61
+ super(ext).tap do |ext_matches|
62
+ convertible.data[:template_engine] = "serbea" if ext_matches
63
+ end
64
+ end
65
+
66
+ def output_ext(ext)
67
+ ext == ".serb" ? ".html" : ext
68
+ end
69
+ end
70
+ end
71
+ end
@@ -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
@@ -31,9 +31,7 @@ module Bridgetown
31
31
  raise Liquid::FileSystemError, "No such template '#{template_path}'" if found_paths.empty?
32
32
 
33
33
  # Last path in the list wins
34
- LiquidComponent.parse(
35
- ::File.read(found_paths.last, **site.file_read_opts)
36
- ).content
34
+ ::File.read(found_paths.last, **site.file_read_opts)
37
35
  end
38
36
  end
39
37
  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