bridgetown-core 0.13.0 → 0.14.0
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.
- checksums.yaml +4 -4
- data/bin/bridgetown +0 -25
- data/bridgetown-core.gemspec +4 -1
- data/lib/bridgetown-core.rb +4 -1
- data/lib/bridgetown-core/cleaner.rb +1 -0
- data/lib/bridgetown-core/command.rb +10 -4
- data/lib/bridgetown-core/commands/console.rb +1 -2
- data/lib/bridgetown-core/commands/doctor.rb +1 -2
- data/lib/bridgetown-core/commands/new.rb +0 -3
- data/lib/bridgetown-core/commands/plugins.rb +169 -0
- data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +2 -2
- data/lib/bridgetown-core/concerns/site/configurable.rb +153 -0
- data/lib/bridgetown-core/concerns/site/content.rb +111 -0
- data/lib/bridgetown-core/concerns/site/extensible.rb +56 -0
- data/lib/bridgetown-core/concerns/site/processable.rb +74 -0
- data/lib/bridgetown-core/concerns/site/renderable.rb +50 -0
- data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
- data/lib/bridgetown-core/configuration.rb +2 -9
- data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +0 -3
- data/lib/bridgetown-core/document.rb +1 -1
- data/lib/bridgetown-core/drops/site_drop.rb +1 -1
- data/lib/bridgetown-core/external.rb +17 -21
- data/lib/bridgetown-core/filters.rb +10 -0
- data/lib/bridgetown-core/generators/prototype_generator.rb +1 -1
- data/lib/bridgetown-core/hooks.rb +62 -62
- data/lib/bridgetown-core/layout.rb +10 -4
- data/lib/bridgetown-core/page.rb +9 -2
- data/lib/bridgetown-core/plugin.rb +2 -0
- data/lib/bridgetown-core/plugin_manager.rb +62 -12
- data/lib/bridgetown-core/reader.rb +5 -0
- data/lib/bridgetown-core/readers/data_reader.rb +5 -2
- data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
- data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
- data/lib/bridgetown-core/renderer.rb +7 -10
- data/lib/bridgetown-core/site.rb +20 -463
- data/lib/bridgetown-core/utils.rb +1 -27
- data/lib/bridgetown-core/utils/ruby_exec.rb +1 -4
- data/lib/bridgetown-core/version.rb +2 -2
- data/lib/bridgetown-core/watcher.rb +5 -1
- data/lib/site_template/plugins/{.keep → builders/.keep} +0 -0
- data/lib/site_template/plugins/site_builder.rb +4 -0
- data/lib/site_template/src/_includes/navbar.html +1 -0
- data/lib/site_template/src/posts.md +15 -0
- data/lib/site_template/start.js +1 -1
- metadata +58 -6
@@ -63,6 +63,16 @@ module Bridgetown
|
|
63
63
|
Utils.slugify(input, mode: mode)
|
64
64
|
end
|
65
65
|
|
66
|
+
# Titleize a slug or identifier string.
|
67
|
+
#
|
68
|
+
# input - The string to titleize.
|
69
|
+
#
|
70
|
+
# Returns a transformed string with spaces and capitalized words.
|
71
|
+
# See Utils.titleize_slug for more detail.
|
72
|
+
def titleize(input)
|
73
|
+
Utils.titleize_slug(input)
|
74
|
+
end
|
75
|
+
|
66
76
|
# XML escape a string for use. Replaces any special characters with
|
67
77
|
# appropriate HTML entity replacements.
|
68
78
|
#
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
Bridgetown::Hooks.register :pages, :post_init do |page|
|
3
|
+
Bridgetown::Hooks.register :pages, :post_init, reloadable: false do |page|
|
4
4
|
if page.class != Bridgetown::PrototypePage && page.data["prototype"].is_a?(Hash)
|
5
5
|
Bridgetown::PrototypeGenerator.add_matching_template(page)
|
6
6
|
end
|
@@ -2,6 +2,19 @@
|
|
2
2
|
|
3
3
|
module Bridgetown
|
4
4
|
module Hooks
|
5
|
+
HookRegistration = Struct.new(
|
6
|
+
:owner,
|
7
|
+
:event,
|
8
|
+
:priority,
|
9
|
+
:reloadable,
|
10
|
+
:block,
|
11
|
+
keyword_init: true
|
12
|
+
) do
|
13
|
+
def to_s
|
14
|
+
"#{owner}:#{event} for #{block}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
5
18
|
DEFAULT_PRIORITY = 20
|
6
19
|
|
7
20
|
# compatibility layer for octopress-hooks users
|
@@ -12,51 +25,11 @@ module Bridgetown
|
|
12
25
|
}.freeze
|
13
26
|
|
14
27
|
# initial empty hooks
|
15
|
-
@registry = {
|
16
|
-
site: {
|
17
|
-
after_init: [],
|
18
|
-
after_reset: [],
|
19
|
-
post_read: [],
|
20
|
-
pre_render: [],
|
21
|
-
post_render: [],
|
22
|
-
post_write: [],
|
23
|
-
},
|
24
|
-
pages: {
|
25
|
-
post_init: [],
|
26
|
-
pre_render: [],
|
27
|
-
post_render: [],
|
28
|
-
post_write: [],
|
29
|
-
},
|
30
|
-
posts: {
|
31
|
-
post_init: [],
|
32
|
-
pre_render: [],
|
33
|
-
post_render: [],
|
34
|
-
post_write: [],
|
35
|
-
},
|
36
|
-
documents: {
|
37
|
-
post_init: [],
|
38
|
-
pre_render: [],
|
39
|
-
post_render: [],
|
40
|
-
post_write: [],
|
41
|
-
},
|
42
|
-
clean: {
|
43
|
-
on_obsolete: [],
|
44
|
-
},
|
45
|
-
}
|
46
|
-
|
47
|
-
# map of all hooks and their priorities
|
48
|
-
@hook_priority = {}
|
28
|
+
@registry = {}
|
49
29
|
|
50
30
|
NotAvailable = Class.new(RuntimeError)
|
51
31
|
Uncallable = Class.new(RuntimeError)
|
52
32
|
|
53
|
-
# register hook(s) to be called later, public API
|
54
|
-
def self.register(owners, event, priority: DEFAULT_PRIORITY, &block)
|
55
|
-
Array(owners).each do |owner|
|
56
|
-
register_one(owner, event, priority_value(priority), &block)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
33
|
# Ensure the priority is a Fixnum
|
61
34
|
def self.priority_value(priority)
|
62
35
|
return priority if priority.is_a?(Integer)
|
@@ -64,39 +37,66 @@ module Bridgetown
|
|
64
37
|
PRIORITY_MAP[priority] || DEFAULT_PRIORITY
|
65
38
|
end
|
66
39
|
|
67
|
-
# register
|
68
|
-
def self.
|
69
|
-
|
70
|
-
|
71
|
-
pre_render: [],
|
72
|
-
post_render: [],
|
73
|
-
post_write: [],
|
74
|
-
}
|
75
|
-
|
76
|
-
unless @registry[owner][event]
|
77
|
-
raise NotAvailable, "Invalid hook. #{owner} supports only the " \
|
78
|
-
"following hooks #{@registry[owner].keys.inspect}"
|
40
|
+
# register hook(s) to be called later
|
41
|
+
def self.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
|
42
|
+
Array(owners).each do |owner|
|
43
|
+
register_one(owner, event, priority: priority, reloadable: reloadable, &block)
|
79
44
|
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# register a single hook to be called later
|
48
|
+
def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
|
49
|
+
@registry[owner] ||= []
|
80
50
|
|
81
51
|
raise Uncallable, "Hooks must respond to :call" unless block.respond_to? :call
|
82
52
|
|
83
|
-
|
53
|
+
@registry[owner] << HookRegistration.new(
|
54
|
+
owner: owner,
|
55
|
+
event: event,
|
56
|
+
priority: priority_value(priority),
|
57
|
+
reloadable: reloadable,
|
58
|
+
block: block
|
59
|
+
)
|
60
|
+
if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
|
61
|
+
if Bridgetown.respond_to?(:logger)
|
62
|
+
Bridgetown.logger.debug("Registering hook:", @registry[owner].last.to_s)
|
63
|
+
else
|
64
|
+
p "Registering hook:", @registry[owner].last.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
block
|
84
69
|
end
|
85
70
|
|
86
|
-
def self.
|
87
|
-
@
|
88
|
-
@registry[owner][event] << block
|
71
|
+
def self.remove_hook(owner, _event, block)
|
72
|
+
@registry[owner].delete_if { |item| item.block == block }
|
89
73
|
end
|
90
74
|
|
91
|
-
# interface for Bridgetown core components to trigger hooks
|
92
75
|
def self.trigger(owner, event, *args)
|
93
76
|
# proceed only if there are hooks to call
|
94
|
-
hooks = @registry
|
77
|
+
hooks = @registry[owner]&.select { |item| item.event == event }
|
95
78
|
return if hooks.nil? || hooks.empty?
|
96
79
|
|
97
|
-
|
98
|
-
|
99
|
-
|
80
|
+
prioritized_hooks(hooks).each do |hook|
|
81
|
+
if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
|
82
|
+
hook_info = args[0]&.respond_to?(:url) ? args[0].relative_path : hook.block
|
83
|
+
Bridgetown.logger.debug("Triggering hook:", "#{owner}:#{event} for #{hook_info}")
|
84
|
+
end
|
85
|
+
hook.block.call(*args)
|
86
|
+
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
|
+
|
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
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -29,14 +29,20 @@ module Bridgetown
|
|
29
29
|
#
|
30
30
|
# site - The Site.
|
31
31
|
# base - The String path to the source.
|
32
|
-
# name - The String filename of the
|
33
|
-
|
32
|
+
# name - The String filename of the layout file.
|
33
|
+
# from_plugin - true if the layout comes from a Gem-based plugin folder.
|
34
|
+
def initialize(site, base, name, from_plugin: false)
|
34
35
|
@site = site
|
35
36
|
@base = base
|
36
37
|
@name = name
|
37
38
|
|
38
|
-
|
39
|
-
|
39
|
+
if from_plugin
|
40
|
+
@base_dir = base.sub("/layouts", "")
|
41
|
+
@path = File.join(base, name)
|
42
|
+
else
|
43
|
+
@base_dir = site.source
|
44
|
+
@path = site.in_source_dir(base, name)
|
45
|
+
end
|
40
46
|
@relative_path = @path.sub(@base_dir, "")
|
41
47
|
|
42
48
|
self.data = {}
|
data/lib/bridgetown-core/page.rb
CHANGED
@@ -35,12 +35,18 @@ module Bridgetown
|
|
35
35
|
# base - The String path to the source.
|
36
36
|
# dir - The String path between the source and the file.
|
37
37
|
# name - The String filename of the file.
|
38
|
-
|
38
|
+
# from_plugin - true if the Page file is located in a Gem-based plugin folder
|
39
|
+
# rubocop:disable Metrics/ParameterLists
|
40
|
+
def initialize(site, base, dir, name, from_plugin: false)
|
39
41
|
@site = site
|
40
42
|
@base = base
|
41
43
|
@dir = dir
|
42
44
|
@name = name
|
43
|
-
@path =
|
45
|
+
@path = if from_plugin
|
46
|
+
File.join(base, dir, name)
|
47
|
+
else
|
48
|
+
site.in_source_dir(base, dir, name)
|
49
|
+
end
|
44
50
|
|
45
51
|
process(name)
|
46
52
|
read_yaml(PathManager.join(base, dir), name)
|
@@ -51,6 +57,7 @@ module Bridgetown
|
|
51
57
|
|
52
58
|
Bridgetown::Hooks.trigger :pages, :post_init, self
|
53
59
|
end
|
60
|
+
# rubocop:enable Metrics/ParameterLists
|
54
61
|
|
55
62
|
# The generated directory into which the page will be placed
|
56
63
|
# upon generation. This is derived from the permalink or, if
|
@@ -4,6 +4,29 @@ module Bridgetown
|
|
4
4
|
class PluginManager
|
5
5
|
attr_reader :site
|
6
6
|
|
7
|
+
@source_manifests = Set.new
|
8
|
+
@registered_plugins = Set.new
|
9
|
+
|
10
|
+
def self.add_source_manifest(source_manifest)
|
11
|
+
unless source_manifest.is_a?(Bridgetown::Plugin::SourceManifest)
|
12
|
+
raise "You must add a SourceManifest instance"
|
13
|
+
end
|
14
|
+
|
15
|
+
@source_manifests << source_manifest
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.new_source_manifest(*args)
|
19
|
+
add_source_manifest(Bridgetown::Plugin::SourceManifest.new(*args))
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.add_registered_plugin(gem_or_plugin_file)
|
23
|
+
@registered_plugins << gem_or_plugin_file
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
attr_reader :source_manifests, :registered_plugins
|
28
|
+
end
|
29
|
+
|
7
30
|
# Create an instance of this class.
|
8
31
|
#
|
9
32
|
# site - the instance of Bridgetown::Site we're concerned with
|
@@ -13,22 +36,25 @@ module Bridgetown
|
|
13
36
|
@site = site
|
14
37
|
end
|
15
38
|
|
16
|
-
# Require all the plugins which are allowed.
|
17
|
-
#
|
18
|
-
# Returns nothing
|
19
|
-
def conscientious_require
|
20
|
-
require_plugin_files
|
21
|
-
end
|
22
|
-
|
23
39
|
def self.require_from_bundler
|
24
40
|
if !ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
|
25
41
|
require "bundler"
|
26
42
|
|
27
|
-
|
28
|
-
|
43
|
+
group_name = :bridgetown_plugins
|
44
|
+
|
45
|
+
required_gems = Bundler.require group_name
|
46
|
+
required_gems.select! do |dep|
|
47
|
+
(dep.groups & [group_name]).any? && dep.should_include?
|
48
|
+
end
|
49
|
+
|
29
50
|
install_yarn_dependencies(required_gems)
|
30
|
-
|
31
|
-
|
51
|
+
|
52
|
+
required_gems.each do |installed_gem|
|
53
|
+
add_registered_plugin installed_gem
|
54
|
+
end
|
55
|
+
|
56
|
+
Bridgetown.logger.debug("PluginManager:",
|
57
|
+
"Required #{required_gems.map(&:name).join(", ")}")
|
32
58
|
ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] = "true"
|
33
59
|
|
34
60
|
true
|
@@ -68,7 +94,31 @@ module Bridgetown
|
|
68
94
|
def require_plugin_files
|
69
95
|
plugins_path.each do |plugin_search_path|
|
70
96
|
plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb"))
|
71
|
-
|
97
|
+
|
98
|
+
# Require "site_builder.rb" first if present so subclasses can all
|
99
|
+
# inherit from SiteBuilder without needing explicit require statements
|
100
|
+
sorted_plugin_files = plugin_files.select do |path|
|
101
|
+
path.include?("site_builder.rb")
|
102
|
+
end + plugin_files.reject do |path|
|
103
|
+
path.include?("site_builder.rb")
|
104
|
+
end
|
105
|
+
|
106
|
+
sorted_plugin_files.each do |plugin_file|
|
107
|
+
self.class.add_registered_plugin plugin_file
|
108
|
+
end
|
109
|
+
Bridgetown::External.require_with_graceful_fail(sorted_plugin_files)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Reload .rb plugin files via the watcher
|
114
|
+
def reload_plugin_files
|
115
|
+
plugins_path.each do |plugin_search_path|
|
116
|
+
plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb"))
|
117
|
+
Array(plugin_files).each do |name|
|
118
|
+
Bridgetown.logger.debug "Reloading:", name.to_s
|
119
|
+
self.class.add_registered_plugin name
|
120
|
+
load name
|
121
|
+
end
|
72
122
|
end
|
73
123
|
end
|
74
124
|
|
@@ -11,6 +11,7 @@ module Bridgetown
|
|
11
11
|
# Read Site data from disk and load it into internal data structures.
|
12
12
|
#
|
13
13
|
# Returns nothing.
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
14
15
|
def read
|
15
16
|
@site.layouts = LayoutReader.new(site).read
|
16
17
|
read_directories
|
@@ -18,7 +19,11 @@ module Bridgetown
|
|
18
19
|
sort_files!
|
19
20
|
@site.data = DataReader.new(site).read(site.config["data_dir"])
|
20
21
|
CollectionReader.new(site).read
|
22
|
+
Bridgetown::PluginManager.source_manifests.map(&:content).compact.each do |plugin_content_dir|
|
23
|
+
PluginContentReader.new(site, plugin_content_dir).read
|
24
|
+
end
|
21
25
|
end
|
26
|
+
# rubocop:enable Metrics/AbcSize
|
22
27
|
|
23
28
|
# Sorts posts, pages, and static files.
|
24
29
|
def sort_files!
|
@@ -5,7 +5,7 @@ module Bridgetown
|
|
5
5
|
attr_reader :site, :content
|
6
6
|
def initialize(site)
|
7
7
|
@site = site
|
8
|
-
@content =
|
8
|
+
@content = ActiveSupport::HashWithIndifferentAccess.new
|
9
9
|
@entry_filter = EntryFilter.new(site)
|
10
10
|
end
|
11
11
|
|
@@ -41,7 +41,10 @@ module Bridgetown
|
|
41
41
|
next if @entry_filter.symlink?(path)
|
42
42
|
|
43
43
|
if File.directory?(path)
|
44
|
-
read_data_to(
|
44
|
+
read_data_to(
|
45
|
+
path,
|
46
|
+
data[sanitize_filename(entry)] = ActiveSupport::HashWithIndifferentAccess.new
|
47
|
+
)
|
45
48
|
else
|
46
49
|
key = sanitize_filename(File.basename(entry, ".*"))
|
47
50
|
data[key] = read_data_file(path)
|
@@ -14,6 +14,13 @@ module Bridgetown
|
|
14
14
|
Layout.new(site, layout_directory, layout_file)
|
15
15
|
end
|
16
16
|
|
17
|
+
Bridgetown::PluginManager.source_manifests.map(&:layouts).compact.each do |plugin_layouts|
|
18
|
+
layout_entries(plugin_layouts).each do |layout_file|
|
19
|
+
@layouts[layout_name(layout_file)] ||= \
|
20
|
+
Layout.new(site, plugin_layouts, layout_file, from_plugin: true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
17
24
|
@layouts
|
18
25
|
end
|
19
26
|
|
@@ -23,8 +30,8 @@ module Bridgetown
|
|
23
30
|
|
24
31
|
private
|
25
32
|
|
26
|
-
def layout_entries
|
27
|
-
entries_in
|
33
|
+
def layout_entries(dir = layout_directory)
|
34
|
+
entries_in dir
|
28
35
|
end
|
29
36
|
|
30
37
|
def entries_in(dir)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bridgetown
|
4
|
+
class PluginContentReader
|
5
|
+
attr_reader :site, :content_dir
|
6
|
+
|
7
|
+
def initialize(site, plugin_content_dir)
|
8
|
+
@site = site
|
9
|
+
@content_dir = plugin_content_dir
|
10
|
+
@content_files = Set.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def read
|
14
|
+
return unless content_dir
|
15
|
+
|
16
|
+
Find.find(content_dir) do |path|
|
17
|
+
next if File.directory?(path)
|
18
|
+
|
19
|
+
if File.symlink?(path)
|
20
|
+
Bridgetown.logger.warn "Plugin content reader:", "Ignored symlinked asset: #{path}"
|
21
|
+
else
|
22
|
+
read_content_file(path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def read_content_file(path)
|
28
|
+
dir = File.dirname(path.sub("#{content_dir}/", ""))
|
29
|
+
name = File.basename(path)
|
30
|
+
|
31
|
+
@content_files << if Utils.has_yaml_header?(path)
|
32
|
+
Bridgetown::Page.new(site, content_dir, dir, name, from_plugin: true)
|
33
|
+
else
|
34
|
+
Bridgetown::StaticFile.new(site, content_dir, "/#{dir}", name)
|
35
|
+
end
|
36
|
+
|
37
|
+
add_to(site.pages, Bridgetown::Page)
|
38
|
+
add_to(site.static_files, Bridgetown::StaticFile)
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_to(content_type, klass)
|
42
|
+
existing_paths = content_type.map(&:relative_path).compact
|
43
|
+
@content_files.select { |item| item.is_a?(klass) }.each do |item|
|
44
|
+
content_type << item unless existing_paths.include?(item.relative_path)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|