bridgetown-core 0.12.1 → 0.15.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
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 +7 -2
  5. data/lib/bridgetown-core.rb +20 -4
  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 +107 -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 -38
  16. data/lib/bridgetown-core/commands/doctor.rb +125 -135
  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 -217
  21. data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +2 -2
  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 +50 -0
  27. data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
  28. data/lib/bridgetown-core/configuration.rb +98 -108
  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/bridgetown_drop.rb +6 -1
  32. data/lib/bridgetown-core/drops/page_drop.rb +1 -1
  33. data/lib/bridgetown-core/drops/site_drop.rb +1 -2
  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 +2 -0
  40. data/lib/bridgetown-core/liquid_renderer/file_system.rb +5 -1
  41. data/lib/bridgetown-core/page.rb +11 -19
  42. data/lib/bridgetown-core/plugin.rb +2 -0
  43. data/lib/bridgetown-core/plugin_manager.rb +68 -14
  44. data/lib/bridgetown-core/reader.rb +5 -0
  45. data/lib/bridgetown-core/readers/data_reader.rb +15 -2
  46. data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
  47. data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
  48. data/lib/bridgetown-core/renderer.rb +51 -32
  49. data/lib/bridgetown-core/site.rb +20 -449
  50. data/lib/bridgetown-core/static_file.rb +1 -5
  51. data/lib/bridgetown-core/tags/include.rb +12 -0
  52. data/lib/bridgetown-core/tags/render_content.rb +27 -16
  53. data/lib/bridgetown-core/tags/with.rb +15 -0
  54. data/lib/bridgetown-core/utils.rb +2 -27
  55. data/lib/bridgetown-core/utils/ruby_exec.rb +66 -0
  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/{_components/.keep → favicon.ico} +0 -0
  67. data/lib/site_template/src/posts.md +15 -0
  68. data/lib/site_template/start.js +1 -1
  69. data/lib/site_template/webpack.config.js +3 -3
  70. metadata +106 -18
  71. data/lib/bridgetown-core/command.rb +0 -106
  72. data/lib/bridgetown-core/commands/help.rb +0 -34
  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
@@ -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,50 @@
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
+ document.output = Bridgetown::Renderer.new(self, document, payload).run
47
+ document.trigger_hooks(:post_render)
48
+ end
49
+ end
50
+ end