bridgetown-core 0.13.0 → 0.15.0.beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -1
  3. data/bin/bridgetown +9 -48
  4. data/bridgetown-core.gemspec +6 -2
  5. data/lib/bridgetown-core.rb +13 -3
  6. data/lib/bridgetown-core/cleaner.rb +1 -0
  7. data/lib/bridgetown-core/commands/apply.rb +73 -0
  8. data/lib/bridgetown-core/commands/base.rb +45 -0
  9. data/lib/bridgetown-core/commands/build.rb +91 -86
  10. data/lib/bridgetown-core/commands/clean.rb +30 -29
  11. data/lib/bridgetown-core/commands/concerns/actions.rb +123 -0
  12. data/lib/bridgetown-core/commands/concerns/build_options.rb +76 -0
  13. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +18 -0
  14. data/lib/bridgetown-core/commands/concerns/summarizable.rb +13 -0
  15. data/lib/bridgetown-core/commands/console.rb +46 -39
  16. data/lib/bridgetown-core/commands/doctor.rb +126 -127
  17. data/lib/bridgetown-core/commands/new.rb +120 -158
  18. data/lib/bridgetown-core/commands/plugins.rb +206 -0
  19. data/lib/bridgetown-core/commands/registrations.rb +16 -0
  20. data/lib/bridgetown-core/commands/serve.rb +214 -215
  21. data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +3 -6
  22. data/lib/bridgetown-core/concerns/site/configurable.rb +153 -0
  23. data/lib/bridgetown-core/concerns/site/content.rb +111 -0
  24. data/lib/bridgetown-core/concerns/site/extensible.rb +56 -0
  25. data/lib/bridgetown-core/concerns/site/processable.rb +74 -0
  26. data/lib/bridgetown-core/concerns/site/renderable.rb +49 -0
  27. data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
  28. data/lib/bridgetown-core/configuration.rb +2 -9
  29. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +0 -3
  30. data/lib/bridgetown-core/document.rb +1 -1
  31. data/lib/bridgetown-core/drops/page_drop.rb +1 -1
  32. data/lib/bridgetown-core/drops/site_drop.rb +1 -1
  33. data/lib/bridgetown-core/excerpt.rb +4 -1
  34. data/lib/bridgetown-core/external.rb +17 -21
  35. data/lib/bridgetown-core/filters.rb +10 -0
  36. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -1
  37. data/lib/bridgetown-core/hooks.rb +62 -62
  38. data/lib/bridgetown-core/layout.rb +10 -4
  39. data/lib/bridgetown-core/liquid_renderer.rb +1 -0
  40. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -4
  41. data/lib/bridgetown-core/liquid_renderer/file_system.rb +3 -1
  42. data/lib/bridgetown-core/page.rb +11 -19
  43. data/lib/bridgetown-core/plugin.rb +2 -0
  44. data/lib/bridgetown-core/plugin_manager.rb +88 -21
  45. data/lib/bridgetown-core/reader.rb +5 -0
  46. data/lib/bridgetown-core/readers/data_reader.rb +5 -2
  47. data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
  48. data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
  49. data/lib/bridgetown-core/renderer.rb +38 -28
  50. data/lib/bridgetown-core/site.rb +20 -463
  51. data/lib/bridgetown-core/tags/include.rb +12 -0
  52. data/lib/bridgetown-core/tags/render_content.rb +29 -16
  53. data/lib/bridgetown-core/tags/with.rb +15 -0
  54. data/lib/bridgetown-core/utils.rb +45 -27
  55. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -4
  56. data/lib/bridgetown-core/version.rb +2 -2
  57. data/lib/bridgetown-core/watcher.rb +21 -10
  58. data/lib/site_template/Gemfile.erb +19 -0
  59. data/lib/site_template/package.json +1 -0
  60. data/lib/site_template/plugins/{.keep → builders/.keep} +0 -0
  61. data/lib/site_template/plugins/site_builder.rb +4 -0
  62. data/lib/site_template/src/_components/footer.html +3 -0
  63. data/lib/site_template/src/_components/head.html +9 -0
  64. data/lib/site_template/src/{_includes → _components}/navbar.html +1 -0
  65. data/lib/site_template/src/_layouts/default.html +3 -3
  66. data/lib/site_template/src/posts.md +15 -0
  67. data/lib/site_template/start.js +1 -1
  68. data/lib/site_template/webpack.config.js +3 -3
  69. metadata +90 -18
  70. data/lib/bridgetown-core/command.rb +0 -106
  71. data/lib/bridgetown-core/commands/help.rb +0 -34
  72. data/lib/site_template/src/_components/.keep +0 -0
  73. data/lib/site_template/src/_includes/footer.html +0 -3
  74. data/lib/site_template/src/_includes/head.html +0 -9
@@ -42,7 +42,7 @@ module Bridgetown
42
42
  **Utils.merged_file_read_opts(site, opts))
43
43
  if content =~ Document::YAML_FRONT_MATTER_REGEXP
44
44
  self.content = $POSTMATCH
45
- self.data = SafeYAML.load(Regexp.last_match(1))
45
+ self.data = SafeYAML.load(Regexp.last_match(1))&.with_indifferent_access
46
46
  end
47
47
  rescue Psych::SyntaxError => e
48
48
  Bridgetown.logger.warn "YAML Exception reading #{filename}: #{e.message}"
@@ -52,7 +52,7 @@ module Bridgetown
52
52
  raise e if site.config["strict_front_matter"]
53
53
  end
54
54
 
55
- self.data ||= {}
55
+ self.data ||= ActiveSupport::HashWithIndifferentAccess.new
56
56
 
57
57
  validate_data! filename
58
58
  validate_permalink! filename
@@ -188,13 +188,10 @@ module Bridgetown
188
188
  #
189
189
  # Returns nothing.
190
190
  def do_layout(payload, layouts)
191
- self.output = _renderer.tap do |renderer|
191
+ _renderer.tap do |renderer|
192
192
  renderer.layouts = layouts
193
193
  renderer.payload = payload
194
194
  end.run
195
-
196
- Bridgetown.logger.debug "Post-Render Hooks:", relative_path
197
- Bridgetown::Hooks.trigger hook_owner, :post_render, self
198
195
  ensure
199
196
  @_renderer = nil # this will allow the modifications above to disappear
200
197
  end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Configurable
5
+ # Public: Set the site's configuration. This handles side-effects caused by
6
+ # changing values in the configuration.
7
+ #
8
+ # config - a Bridgetown::Configuration, containing the new configuration.
9
+ #
10
+ # Returns the new configuration.
11
+ def config=(config)
12
+ @config = config.clone
13
+
14
+ # Source and destination may not be changed after the site has been created.
15
+ @root_dir = File.expand_path(config["root_dir"]).freeze
16
+ @source = File.expand_path(config["source"]).freeze
17
+ @dest = File.expand_path(config["destination"]).freeze
18
+ @cache_dir = in_root_dir(config["cache_dir"]).freeze
19
+
20
+ %w(lsi highlighter baseurl exclude include future unpublished
21
+ limit_posts keep_files).each do |opt|
22
+ send("#{opt}=", config[opt])
23
+ end
24
+
25
+ configure_cache
26
+ configure_component_paths
27
+ configure_include_paths
28
+ configure_file_read_opts
29
+
30
+ self.permalink_style = config["permalink"].to_sym
31
+
32
+ @config
33
+ end
34
+
35
+ # Returns the FrontmatterDefaults or creates a new FrontmatterDefaults
36
+ # if it doesn't already exist.
37
+ #
38
+ # Returns The FrontmatterDefaults
39
+ def frontmatter_defaults
40
+ @frontmatter_defaults ||= FrontmatterDefaults.new(self)
41
+ end
42
+
43
+ # Whether to perform a full rebuild without incremental regeneration
44
+ #
45
+ # Returns a Boolean: true for a full rebuild, false for normal build
46
+ def incremental?(override = {})
47
+ override["incremental"] || config["incremental"]
48
+ end
49
+
50
+ # Returns the publisher or creates a new publisher if it doesn't
51
+ # already exist.
52
+ #
53
+ # Returns The Publisher
54
+ def publisher
55
+ @publisher ||= Publisher.new(self)
56
+ end
57
+
58
+ # Public: Prefix a given path with the root directory.
59
+ #
60
+ # paths - (optional) path elements to a file or directory within the
61
+ # root directory
62
+ #
63
+ # Returns a path which is prefixed with the root_dir directory.
64
+ def in_root_dir(*paths)
65
+ paths.reduce(root_dir) do |base, path|
66
+ Bridgetown.sanitized_path(base, path)
67
+ end
68
+ end
69
+
70
+ # Public: Prefix a given path with the source directory.
71
+ #
72
+ # paths - (optional) path elements to a file or directory within the
73
+ # source directory
74
+ #
75
+ # Returns a path which is prefixed with the source directory.
76
+ def in_source_dir(*paths)
77
+ paths.reduce(source) do |base, path|
78
+ Bridgetown.sanitized_path(base, path)
79
+ end
80
+ end
81
+
82
+ # Public: Prefix a given path with the destination directory.
83
+ #
84
+ # paths - (optional) path elements to a file or directory within the
85
+ # destination directory
86
+ #
87
+ # Returns a path which is prefixed with the destination directory.
88
+ def in_dest_dir(*paths)
89
+ paths.reduce(dest) do |base, path|
90
+ Bridgetown.sanitized_path(base, path)
91
+ end
92
+ end
93
+
94
+ # Public: Prefix a given path with the cache directory.
95
+ #
96
+ # paths - (optional) path elements to a file or directory within the
97
+ # cache directory
98
+ #
99
+ # Returns a path which is prefixed with the cache directory.
100
+ def in_cache_dir(*paths)
101
+ paths.reduce(cache_dir) do |base, path|
102
+ Bridgetown.sanitized_path(base, path)
103
+ end
104
+ end
105
+
106
+ # Public: The full path to the directory that houses all the collections registered
107
+ # with the current site.
108
+ #
109
+ # Returns the source directory or the absolute path to the custom collections_dir
110
+ def collections_path
111
+ dir_str = config["collections_dir"]
112
+ @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
113
+ end
114
+
115
+ private
116
+
117
+ # Disable Marshaling cache to disk in Safe Mode
118
+ def configure_cache
119
+ Bridgetown::Cache.cache_dir = in_root_dir(config["cache_dir"], "Bridgetown/Cache")
120
+ Bridgetown::Cache.disable_disk_cache! if config["disable_disk_cache"]
121
+ end
122
+
123
+ def configure_component_paths
124
+ # Loop through plugins paths first
125
+ plugin_components_load_paths = Bridgetown::PluginManager.source_manifests
126
+ .map(&:components).compact
127
+
128
+ local_components_load_paths = config["components_dir"].yield_self do |dir|
129
+ dir.is_a?(Array) ? dir : [dir]
130
+ end
131
+ local_components_load_paths.map! do |dir|
132
+ if !!(dir =~ %r!^\.\.?\/!)
133
+ # allow ./dir or ../../dir type options
134
+ File.expand_path(dir.to_s, root_dir)
135
+ else
136
+ in_source_dir(dir.to_s)
137
+ end
138
+ end
139
+
140
+ @components_load_paths = plugin_components_load_paths + local_components_load_paths
141
+ end
142
+
143
+ def configure_include_paths
144
+ @includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
145
+ end
146
+
147
+ def configure_file_read_opts
148
+ self.file_read_opts = {}
149
+ file_read_opts[:encoding] = config["encoding"] if config["encoding"]
150
+ self.file_read_opts = Bridgetown::Utils.merged_file_read_opts(self, {})
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Content
5
+ # Construct a Hash of Posts indexed by the specified Post attribute.
6
+ #
7
+ # post_attr - The String name of the Post attribute.
8
+ #
9
+ # Examples
10
+ #
11
+ # post_attr_hash('categories')
12
+ # # => { 'tech' => [<Post A>, <Post B>],
13
+ # # 'ruby' => [<Post B>] }
14
+ #
15
+ # Returns the Hash: { attr => posts } where
16
+ # attr - One of the values for the requested attribute.
17
+ # posts - The Array of Posts with the given attr value.
18
+ def post_attr_hash(post_attr)
19
+ # Build a hash map based on the specified post attribute ( post attr =>
20
+ # array of posts ) then sort each array in reverse order.
21
+ @post_attr_hash[post_attr] ||= begin
22
+ hash = Hash.new { |h, key| h[key] = [] }
23
+ posts.docs.each do |p|
24
+ p.data[post_attr]&.each { |t| hash[t] << p }
25
+ end
26
+ hash.each_value { |posts| posts.sort!.reverse! }
27
+ hash
28
+ end
29
+ end
30
+
31
+ def tags
32
+ post_attr_hash("tags")
33
+ end
34
+
35
+ def categories
36
+ post_attr_hash("categories")
37
+ end
38
+
39
+ def metadata
40
+ data["site_metadata"] ||= ActiveSupport::HashWithIndifferentAccess.new
41
+ end
42
+
43
+ # The Hash payload containing site-wide data.
44
+ #
45
+ # Returns the Hash: { "site" => data } where data is a Hash with keys:
46
+ # "time" - The Time as specified in the configuration or the
47
+ # current time if none was specified.
48
+ # "posts" - The Array of Posts, sorted chronologically by post date
49
+ # and then title.
50
+ # "pages" - The Array of all Pages.
51
+ # "html_pages" - The Array of HTML Pages.
52
+ # "categories" - The Hash of category values and Posts.
53
+ # See Site#post_attr_hash for type info.
54
+ # "tags" - The Hash of tag values and Posts.
55
+ # See Site#post_attr_hash for type info.
56
+ def site_payload
57
+ Drops::UnifiedPayloadDrop.new self
58
+ end
59
+ alias_method :to_liquid, :site_payload
60
+
61
+ # The list of collections and their corresponding Bridgetown::Collection instances.
62
+ # If config['collections'] is set, a new instance is created
63
+ # for each item in the collection, a new hash is returned otherwise.
64
+ #
65
+ # Returns a Hash containing collection name-to-instance pairs.
66
+ def collections
67
+ @collections ||= collection_names.each_with_object(
68
+ ActiveSupport::HashWithIndifferentAccess.new
69
+ ) do |name, hsh|
70
+ hsh[name] = Bridgetown::Collection.new(self, name)
71
+ end
72
+ end
73
+
74
+ # The list of collection names.
75
+ #
76
+ # Returns an array of collection names from the configuration,
77
+ # or an empty array if the `collections` key is not set.
78
+ def collection_names
79
+ case config["collections"]
80
+ when Hash
81
+ config["collections"].keys
82
+ when Array
83
+ config["collections"]
84
+ when nil
85
+ []
86
+ else
87
+ raise ArgumentError, "Your `collections` key must be a hash or an array."
88
+ end
89
+ end
90
+
91
+ # Get all the documents
92
+ #
93
+ # Returns an Array of all Documents
94
+ def documents
95
+ collections.each_with_object(Set.new) do |(_, collection), set|
96
+ set.merge(collection.docs).merge(collection.files)
97
+ end.to_a
98
+ end
99
+
100
+ # Get the to be written documents
101
+ #
102
+ # Returns an Array of Documents which should be written
103
+ def docs_to_write
104
+ documents.select(&:write?)
105
+ end
106
+
107
+ def posts
108
+ collections["posts"] ||= Collection.new(self, "posts")
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Extensible
5
+ # Load necessary libraries, plugins, converters, and generators.
6
+ #
7
+ # Returns nothing.
8
+ def setup
9
+ plugin_manager.require_plugin_files
10
+ self.converters = instantiate_subclasses(Bridgetown::Converter)
11
+ self.generators = instantiate_subclasses(Bridgetown::Generator)
12
+ end
13
+
14
+ # Run each of the Generators.
15
+ #
16
+ # Returns nothing.
17
+ def generate
18
+ generators.each do |generator|
19
+ start = Time.now
20
+ generator.generate(self)
21
+
22
+ next unless ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
23
+
24
+ generator_name = if generator.class.respond_to?(:custom_name)
25
+ generator.class.custom_name
26
+ else
27
+ generator.class.name
28
+ end
29
+ Bridgetown.logger.debug "Generating:",
30
+ "#{generator_name} finished in #{Time.now - start} seconds."
31
+ end
32
+ end
33
+
34
+ # Get the implementation class for the given Converter.
35
+ # Returns the Converter instance implementing the given Converter.
36
+ # klass - The Class of the Converter to fetch.
37
+ def find_converter_instance(klass)
38
+ @find_converter_instance ||= {}
39
+ @find_converter_instance[klass] ||= begin
40
+ converters.find { |converter| converter.instance_of?(klass) } || \
41
+ raise("No Converters found for #{klass}")
42
+ end
43
+ end
44
+
45
+ # klass - class or module containing the subclasses.
46
+ # Returns array of instances of subclasses of parameter.
47
+ # Create array of instances of the subclasses of the class or module
48
+ # passed in as argument.
49
+
50
+ def instantiate_subclasses(klass)
51
+ klass.descendants.sort.map do |c|
52
+ c.new(config)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Processable
5
+ # Public: Read, process, and write this Site to output.
6
+ #
7
+ # Returns nothing.
8
+ def process
9
+ reset
10
+ read
11
+ generate # Extensible
12
+ render # Renderable
13
+ cleanup # Writable
14
+ write # Writable
15
+ print_stats if config["profile"]
16
+ end
17
+
18
+ # rubocop:disable Metrics/AbcSize
19
+ #
20
+ # Reset Site details.
21
+ #
22
+ # Returns nothing
23
+ def reset
24
+ self.time = if config["time"]
25
+ Utils.parse_date(config["time"].to_s, "Invalid time in bridgetown.config.yml.")
26
+ else
27
+ Time.now
28
+ end
29
+ self.layouts = ActiveSupport::HashWithIndifferentAccess.new
30
+ self.pages = []
31
+ self.static_files = []
32
+ self.data = ActiveSupport::HashWithIndifferentAccess.new
33
+ @post_attr_hash = {}
34
+ @collections = nil
35
+ @documents = nil
36
+ @docs_to_write = nil
37
+ @regenerator.clear_cache
38
+ @liquid_renderer.reset
39
+ frontmatter_defaults.reset
40
+
41
+ raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
42
+
43
+ Bridgetown::Cache.clear_if_config_changed config
44
+ Bridgetown::Hooks.trigger :site, :after_reset, self
45
+ end
46
+ # rubocop:enable Metrics/AbcSize
47
+
48
+ # Read Site data from disk and load it into internal data structures.
49
+ #
50
+ # Returns nothing.
51
+ def read
52
+ Bridgetown::Hooks.trigger :site, :pre_read, self
53
+ reader.read
54
+ limit_posts!
55
+ Bridgetown::Hooks.trigger :site, :post_read, self
56
+ end
57
+
58
+ private
59
+
60
+ # Limits the current posts; removes the posts which exceed the limit_posts
61
+ #
62
+ # Returns nothing
63
+ def limit_posts!
64
+ if limit_posts.positive?
65
+ limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
66
+ posts.docs = posts.docs[-limit, limit]
67
+ end
68
+ end
69
+
70
+ def print_stats
71
+ Bridgetown.logger.info @liquid_renderer.stats_table
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Renderable
5
+ # Render the site to the destination.
6
+ #
7
+ # Returns nothing.
8
+ def render
9
+ payload = site_payload
10
+
11
+ Bridgetown::Hooks.trigger :site, :pre_render, self, payload
12
+
13
+ execute_inline_ruby_for_layouts!
14
+
15
+ render_docs(payload)
16
+ render_pages(payload)
17
+
18
+ Bridgetown::Hooks.trigger :site, :post_render, self, payload
19
+ end
20
+
21
+ def execute_inline_ruby_for_layouts!
22
+ return unless config.should_execute_inline_ruby?
23
+
24
+ layouts.each_value do |layout|
25
+ Bridgetown::Utils::RubyExec.search_data_for_ruby_code(layout, self)
26
+ end
27
+ end
28
+
29
+ def render_docs(payload)
30
+ collections.each_value do |collection|
31
+ collection.docs.each do |document|
32
+ render_regenerated(document, payload)
33
+ end
34
+ end
35
+ end
36
+
37
+ def render_pages(payload)
38
+ pages.each do |page|
39
+ render_regenerated(page, payload)
40
+ end
41
+ end
42
+
43
+ def render_regenerated(document, payload)
44
+ return unless regenerator.regenerate?(document)
45
+
46
+ Bridgetown::Renderer.new(self, document, payload).run
47
+ end
48
+ end
49
+ end