bridgetown-core 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +42 -0
  3. data/bridgetown-core.gemspec +46 -0
  4. data/lib/bridgetown-core.rb +202 -0
  5. data/lib/bridgetown-core/cache.rb +190 -0
  6. data/lib/bridgetown-core/cleaner.rb +111 -0
  7. data/lib/bridgetown-core/collection.rb +279 -0
  8. data/lib/bridgetown-core/command.rb +106 -0
  9. data/lib/bridgetown-core/commands/build.rb +96 -0
  10. data/lib/bridgetown-core/commands/clean.rb +43 -0
  11. data/lib/bridgetown-core/commands/console.rb +56 -0
  12. data/lib/bridgetown-core/commands/doctor.rb +172 -0
  13. data/lib/bridgetown-core/commands/help.rb +34 -0
  14. data/lib/bridgetown-core/commands/new.rb +148 -0
  15. data/lib/bridgetown-core/commands/serve.rb +273 -0
  16. data/lib/bridgetown-core/commands/serve/servlet.rb +68 -0
  17. data/lib/bridgetown-core/configuration.rb +323 -0
  18. data/lib/bridgetown-core/converter.rb +54 -0
  19. data/lib/bridgetown-core/converters/identity.rb +39 -0
  20. data/lib/bridgetown-core/converters/markdown.rb +108 -0
  21. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +132 -0
  22. data/lib/bridgetown-core/converters/smartypants.rb +69 -0
  23. data/lib/bridgetown-core/convertible.rb +237 -0
  24. data/lib/bridgetown-core/deprecator.rb +50 -0
  25. data/lib/bridgetown-core/document.rb +475 -0
  26. data/lib/bridgetown-core/drops/bridgetown_drop.rb +32 -0
  27. data/lib/bridgetown-core/drops/collection_drop.rb +20 -0
  28. data/lib/bridgetown-core/drops/document_drop.rb +69 -0
  29. data/lib/bridgetown-core/drops/drop.rb +215 -0
  30. data/lib/bridgetown-core/drops/excerpt_drop.rb +19 -0
  31. data/lib/bridgetown-core/drops/page_drop.rb +14 -0
  32. data/lib/bridgetown-core/drops/site_drop.rb +62 -0
  33. data/lib/bridgetown-core/drops/static_file_drop.rb +14 -0
  34. data/lib/bridgetown-core/drops/unified_payload_drop.rb +26 -0
  35. data/lib/bridgetown-core/drops/url_drop.rb +132 -0
  36. data/lib/bridgetown-core/entry_filter.rb +108 -0
  37. data/lib/bridgetown-core/errors.rb +20 -0
  38. data/lib/bridgetown-core/excerpt.rb +202 -0
  39. data/lib/bridgetown-core/external.rb +62 -0
  40. data/lib/bridgetown-core/filters.rb +467 -0
  41. data/lib/bridgetown-core/filters/date_filters.rb +110 -0
  42. data/lib/bridgetown-core/filters/grouping_filters.rb +64 -0
  43. data/lib/bridgetown-core/filters/url_filters.rb +79 -0
  44. data/lib/bridgetown-core/frontmatter_defaults.rb +238 -0
  45. data/lib/bridgetown-core/generator.rb +5 -0
  46. data/lib/bridgetown-core/hooks.rb +103 -0
  47. data/lib/bridgetown-core/layout.rb +57 -0
  48. data/lib/bridgetown-core/liquid_extensions.rb +22 -0
  49. data/lib/bridgetown-core/liquid_renderer.rb +71 -0
  50. data/lib/bridgetown-core/liquid_renderer/file.rb +67 -0
  51. data/lib/bridgetown-core/liquid_renderer/table.rb +75 -0
  52. data/lib/bridgetown-core/log_adapter.rb +151 -0
  53. data/lib/bridgetown-core/log_writer.rb +60 -0
  54. data/lib/bridgetown-core/mime.types +867 -0
  55. data/lib/bridgetown-core/page.rb +214 -0
  56. data/lib/bridgetown-core/page_without_a_file.rb +14 -0
  57. data/lib/bridgetown-core/path_manager.rb +31 -0
  58. data/lib/bridgetown-core/plugin.rb +80 -0
  59. data/lib/bridgetown-core/plugin_manager.rb +60 -0
  60. data/lib/bridgetown-core/publisher.rb +23 -0
  61. data/lib/bridgetown-core/reader.rb +185 -0
  62. data/lib/bridgetown-core/readers/collection_reader.rb +22 -0
  63. data/lib/bridgetown-core/readers/data_reader.rb +75 -0
  64. data/lib/bridgetown-core/readers/layout_reader.rb +48 -0
  65. data/lib/bridgetown-core/readers/page_reader.rb +24 -0
  66. data/lib/bridgetown-core/readers/post_reader.rb +74 -0
  67. data/lib/bridgetown-core/readers/static_file_reader.rb +24 -0
  68. data/lib/bridgetown-core/regenerator.rb +195 -0
  69. data/lib/bridgetown-core/related_posts.rb +52 -0
  70. data/lib/bridgetown-core/renderer.rb +261 -0
  71. data/lib/bridgetown-core/site.rb +469 -0
  72. data/lib/bridgetown-core/static_file.rb +205 -0
  73. data/lib/bridgetown-core/tags/component.rb +34 -0
  74. data/lib/bridgetown-core/tags/highlight.rb +111 -0
  75. data/lib/bridgetown-core/tags/include.rb +220 -0
  76. data/lib/bridgetown-core/tags/link.rb +41 -0
  77. data/lib/bridgetown-core/tags/post_url.rb +107 -0
  78. data/lib/bridgetown-core/url.rb +164 -0
  79. data/lib/bridgetown-core/utils.rb +367 -0
  80. data/lib/bridgetown-core/utils/ansi.rb +57 -0
  81. data/lib/bridgetown-core/utils/exec.rb +26 -0
  82. data/lib/bridgetown-core/utils/internet.rb +37 -0
  83. data/lib/bridgetown-core/utils/platforms.rb +80 -0
  84. data/lib/bridgetown-core/utils/thread_event.rb +31 -0
  85. data/lib/bridgetown-core/utils/win_tz.rb +75 -0
  86. data/lib/bridgetown-core/version.rb +5 -0
  87. data/lib/bridgetown-core/watcher.rb +139 -0
  88. data/lib/site_template/.gitignore +6 -0
  89. data/lib/site_template/bridgetown.config.yml +21 -0
  90. data/lib/site_template/frontend/javascript/index.js +3 -0
  91. data/lib/site_template/frontend/styles/index.scss +17 -0
  92. data/lib/site_template/package.json +23 -0
  93. data/lib/site_template/src/404.html +9 -0
  94. data/lib/site_template/src/_data/site_metadata.yml +11 -0
  95. data/lib/site_template/src/_includes/footer.html +3 -0
  96. data/lib/site_template/src/_includes/head.html +9 -0
  97. data/lib/site_template/src/_includes/navbar.html +4 -0
  98. data/lib/site_template/src/_layouts/default.html +15 -0
  99. data/lib/site_template/src/_layouts/home.html +7 -0
  100. data/lib/site_template/src/_layouts/page.html +7 -0
  101. data/lib/site_template/src/_layouts/post.html +7 -0
  102. data/lib/site_template/src/_posts/0000-00-00-welcome-to-bridgetown.md.erb +26 -0
  103. data/lib/site_template/src/about.md +11 -0
  104. data/lib/site_template/src/index.md +7 -0
  105. data/lib/site_template/webpack.config.js +60 -0
  106. data/rake/release.rake +30 -0
  107. metadata +106 -1
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Drops
5
+ class UnifiedPayloadDrop < Drop
6
+ mutable true
7
+
8
+ attr_accessor :page, :layout, :content, :paginator
9
+ attr_accessor :highlighter_prefix, :highlighter_suffix
10
+
11
+ def bridgetown
12
+ BridgetownDrop.global
13
+ end
14
+
15
+ def site
16
+ @site_drop ||= SiteDrop.new(@obj)
17
+ end
18
+
19
+ private
20
+
21
+ def fallback_data
22
+ @fallback_data ||= {}
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Drops
5
+ class UrlDrop < Drop
6
+ extend Forwardable
7
+
8
+ mutable false
9
+
10
+ def_delegator :@obj, :cleaned_relative_path, :path
11
+ def_delegator :@obj, :output_ext, :output_ext
12
+
13
+ def collection
14
+ @obj.collection.label
15
+ end
16
+
17
+ def name
18
+ Utils.slugify(@obj.basename_without_ext)
19
+ end
20
+
21
+ def title
22
+ Utils.slugify(@obj.data["slug"], :mode => "pretty", :cased => true) ||
23
+ Utils.slugify(@obj.basename_without_ext, :mode => "pretty", :cased => true)
24
+ end
25
+
26
+ def slug
27
+ Utils.slugify(@obj.data["slug"]) || Utils.slugify(@obj.basename_without_ext)
28
+ end
29
+
30
+ def categories
31
+ category_set = Set.new
32
+ Array(@obj.data["categories"]).each do |category|
33
+ category_set << category.to_s.downcase
34
+ end
35
+ category_set.to_a.join("/")
36
+ end
37
+
38
+ # CCYY
39
+ def year
40
+ @obj.date.strftime("%Y")
41
+ end
42
+
43
+ # MM: 01..12
44
+ def month
45
+ @obj.date.strftime("%m")
46
+ end
47
+
48
+ # DD: 01..31
49
+ def day
50
+ @obj.date.strftime("%d")
51
+ end
52
+
53
+ # hh: 00..23
54
+ def hour
55
+ @obj.date.strftime("%H")
56
+ end
57
+
58
+ # mm: 00..59
59
+ def minute
60
+ @obj.date.strftime("%M")
61
+ end
62
+
63
+ # ss: 00..59
64
+ def second
65
+ @obj.date.strftime("%S")
66
+ end
67
+
68
+ # D: 1..31
69
+ def i_day
70
+ @obj.date.strftime("%-d")
71
+ end
72
+
73
+ # M: 1..12
74
+ def i_month
75
+ @obj.date.strftime("%-m")
76
+ end
77
+
78
+ # MMM: Jan..Dec
79
+ def short_month
80
+ @obj.date.strftime("%b")
81
+ end
82
+
83
+ # MMMM: January..December
84
+ def long_month
85
+ @obj.date.strftime("%B")
86
+ end
87
+
88
+ # YY: 00..99
89
+ def short_year
90
+ @obj.date.strftime("%y")
91
+ end
92
+
93
+ # CCYYw, ISO week year
94
+ # may differ from CCYY for the first days of January and last days of December
95
+ def w_year
96
+ @obj.date.strftime("%G")
97
+ end
98
+
99
+ # WW: 01..53
100
+ # %W and %U do not comply with ISO 8601-1
101
+ def week
102
+ @obj.date.strftime("%V")
103
+ end
104
+
105
+ # d: 1..7 (Monday..Sunday)
106
+ def w_day
107
+ @obj.date.strftime("%u")
108
+ end
109
+
110
+ # dd: Mon..Sun
111
+ def short_day
112
+ @obj.date.strftime("%a")
113
+ end
114
+
115
+ # ddd: Monday..Sunday
116
+ def long_day
117
+ @obj.date.strftime("%A")
118
+ end
119
+
120
+ # DDD: 001..366
121
+ def y_day
122
+ @obj.date.strftime("%j")
123
+ end
124
+
125
+ private
126
+
127
+ def fallback_data
128
+ @fallback_data ||= {}
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class EntryFilter
5
+ attr_reader :site
6
+ SPECIAL_LEADING_CHAR_REGEX = %r!\A#{Regexp.union([".", "_", "#", "~"])}!o.freeze
7
+
8
+ def initialize(site, base_directory = nil)
9
+ @site = site
10
+ @base_directory = derive_base_directory(
11
+ @site, base_directory.to_s.dup
12
+ )
13
+ end
14
+
15
+ def base_directory
16
+ @base_directory.to_s
17
+ end
18
+
19
+ def derive_base_directory(site, base_dir)
20
+ base_dir[site.source] = "" if base_dir.start_with?(site.source)
21
+ base_dir
22
+ end
23
+
24
+ def relative_to_source(entry)
25
+ File.join(
26
+ base_directory, entry
27
+ )
28
+ end
29
+
30
+ def filter(entries)
31
+ entries.reject do |e|
32
+ # Reject this entry if it is just a "dot" representation.
33
+ # e.g.: '.', '..', '_movies/.', 'music/..', etc
34
+ next true if e.end_with?(".")
35
+ # Reject this entry if it is a symlink.
36
+ next true if symlink?(e)
37
+ # Do not reject this entry if it is included.
38
+ next false if included?(e)
39
+
40
+ # Reject this entry if it is special, a backup file, or excluded.
41
+ special?(e) || backup?(e) || excluded?(e)
42
+ end
43
+ end
44
+
45
+ def included?(entry)
46
+ glob_include?(site.include, entry) ||
47
+ glob_include?(site.include, File.basename(entry))
48
+ end
49
+
50
+ def special?(entry)
51
+ SPECIAL_LEADING_CHAR_REGEX.match?(entry) ||
52
+ SPECIAL_LEADING_CHAR_REGEX.match?(File.basename(entry))
53
+ end
54
+
55
+ def backup?(entry)
56
+ entry.end_with?("~")
57
+ end
58
+
59
+ def excluded?(entry)
60
+ glob_include?(site.exclude - site.include, relative_to_source(entry)).tap do |excluded|
61
+ if excluded
62
+ Bridgetown.logger.debug(
63
+ "EntryFilter:",
64
+ "excluded #{relative_to_source(entry)}"
65
+ )
66
+ end
67
+ end
68
+ end
69
+
70
+ # --
71
+ # TODO: this is for old Safe mode and can be removed.
72
+ # --
73
+ def symlink?(_entry)
74
+ false
75
+ end
76
+
77
+ # --
78
+ # NOTE: Pathutil#in_path? gets the realpath.
79
+ # @param [<Anything>] entry the entry you want to validate.
80
+ # Check if a path is outside of our given root.
81
+ # --
82
+ def symlink_outside_site_source?(entry)
83
+ !Pathutil.new(entry).in_path?(
84
+ site.in_source_dir
85
+ )
86
+ end
87
+
88
+ # Check if an entry matches a specific pattern.
89
+ # Returns true if path matches against any glob pattern, else false.
90
+ def glob_include?(enumerator, entry)
91
+ entry_with_source = PathManager.join(site.source, entry)
92
+
93
+ enumerator.any? do |pattern|
94
+ case pattern
95
+ when String
96
+ pattern_with_source = PathManager.join(site.source, pattern)
97
+
98
+ File.fnmatch?(pattern_with_source, entry_with_source) ||
99
+ entry_with_source.start_with?(pattern_with_source)
100
+ when Regexp
101
+ pattern.match?(entry_with_source)
102
+ else
103
+ false
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Errors
5
+ FatalException = Class.new(::RuntimeError)
6
+
7
+ InvalidThemeName = Class.new(FatalException)
8
+
9
+ DropMutationException = Class.new(FatalException)
10
+ InvalidPermalinkError = Class.new(FatalException)
11
+ InvalidYAMLFrontMatterError = Class.new(FatalException)
12
+ MissingDependencyException = Class.new(FatalException)
13
+
14
+ InvalidDateError = Class.new(FatalException)
15
+ InvalidPostNameError = Class.new(FatalException)
16
+ PostURLError = Class.new(FatalException)
17
+ InvalidURLError = Class.new(FatalException)
18
+ InvalidConfigurationError = Class.new(FatalException)
19
+ end
20
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class Excerpt
5
+ extend Forwardable
6
+
7
+ attr_accessor :doc
8
+ attr_accessor :content, :ext
9
+ attr_writer :output
10
+
11
+ def_delegators :@doc,
12
+ :site, :name, :ext, :extname,
13
+ :collection, :related_posts, :type,
14
+ :yaml_file?,
15
+ :url, :next_doc, :previous_doc
16
+
17
+ private :yaml_file?
18
+
19
+ # Initialize this Excerpt instance.
20
+ #
21
+ # doc - The Document.
22
+ #
23
+ # Returns the new Excerpt.
24
+ def initialize(doc)
25
+ self.doc = doc
26
+ self.content = extract_excerpt(doc.content)
27
+ end
28
+
29
+ # Fetch YAML front-matter data from related doc, without layout key
30
+ #
31
+ # Returns Hash of doc data
32
+ def data
33
+ @data ||= doc.data.dup
34
+ @data.delete("layout")
35
+ @data.delete("excerpt")
36
+ @data
37
+ end
38
+
39
+ def trigger_hooks(*); end
40
+
41
+ # 'Path' of the excerpt.
42
+ #
43
+ # Returns the path for the doc this excerpt belongs to with #excerpt appended
44
+ def path
45
+ File.join(doc.path, "#excerpt")
46
+ end
47
+
48
+ # 'Relative Path' of the excerpt.
49
+ #
50
+ # Returns the relative_path for the doc this excerpt belongs to with #excerpt appended
51
+ def relative_path
52
+ @relative_path ||= File.join(doc.relative_path, "#excerpt")
53
+ end
54
+
55
+ # Check if excerpt includes a string
56
+ #
57
+ # Returns true if the string passed in
58
+ def include?(something)
59
+ output&.include?(something) || content.include?(something)
60
+ end
61
+
62
+ # The UID for this doc (useful in feeds).
63
+ # e.g. /2008/11/05/my-awesome-doc
64
+ #
65
+ # Returns the String UID.
66
+ def id
67
+ "#{doc.id}#excerpt"
68
+ end
69
+
70
+ def to_s
71
+ output || content
72
+ end
73
+
74
+ def to_liquid
75
+ Bridgetown::Drops::ExcerptDrop.new(self)
76
+ end
77
+
78
+ # Returns the shorthand String identifier of this doc.
79
+ def inspect
80
+ "<#{self.class} id=#{id}>"
81
+ end
82
+
83
+ def output
84
+ @output ||= Renderer.new(doc.site, self, site.site_payload).run
85
+ end
86
+
87
+ def place_in_layout?
88
+ false
89
+ end
90
+
91
+ def render_with_liquid?
92
+ return false if data["render_with_liquid"] == false
93
+
94
+ !(yaml_file? || !Utils.has_liquid_construct?(content))
95
+ end
96
+
97
+ protected
98
+
99
+ # Internal: Extract excerpt from the content
100
+ #
101
+ # By default excerpt is your first paragraph of a doc: everything before
102
+ # the first two new lines:
103
+ #
104
+ # ---
105
+ # title: Example
106
+ # ---
107
+ #
108
+ # First paragraph with [link][1].
109
+ #
110
+ # Second paragraph.
111
+ #
112
+ # [1]: http://example.com/
113
+ #
114
+ # This is fairly good option for Markdown and Textile files. But might cause
115
+ # problems for HTML docs (which is quite unusual for Bridgetown). If default
116
+ # excerpt delimiter is not good for you, you might want to set your own via
117
+ # configuration option `excerpt_separator`. For example, following is a good
118
+ # alternative for HTML docs:
119
+ #
120
+ # # file: bridgetown.config.yml
121
+ # excerpt_separator: "<!-- more -->"
122
+ #
123
+ # Notice that all markdown-style link references will be appended to the
124
+ # excerpt. So the example doc above will have this excerpt source:
125
+ #
126
+ # First paragraph with [link][1].
127
+ #
128
+ # [1]: http://example.com/
129
+ #
130
+ # Excerpts are rendered same time as content is rendered.
131
+ #
132
+ # Returns excerpt String
133
+
134
+ LIQUID_TAG_REGEX = %r!{%-?\s*(\w+)\s*.*?-?%}!m.freeze
135
+ MKDWN_LINK_REF_REGEX = %r!^ {0,3}(?:(\[[^\]]+\])(:.+))$!.freeze
136
+
137
+ def extract_excerpt(doc_content)
138
+ head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
139
+ return head if tail.empty?
140
+
141
+ head = sanctify_liquid_tags(head) if head.include?("{%")
142
+ definitions = extract_markdown_link_reference_defintions(head, tail)
143
+ return head if definitions.empty?
144
+
145
+ head << "\n\n" << definitions.join("\n")
146
+ end
147
+
148
+ private
149
+
150
+ # append appropriate closing tag(s) (for each Liquid block), to the `head` if the
151
+ # partitioning resulted in leaving the closing tag somewhere in the `tail` partition.
152
+ def sanctify_liquid_tags(head)
153
+ modified = false
154
+ tag_names = head.scan(LIQUID_TAG_REGEX)
155
+ tag_names.flatten!
156
+ tag_names.reverse_each do |tag_name|
157
+ next unless liquid_block?(tag_name)
158
+ next if endtag_regex_stash(tag_name).match?(head)
159
+
160
+ modified = true
161
+ head << "\n{% end#{tag_name} %}"
162
+ end
163
+
164
+ print_build_warning if modified
165
+ head
166
+ end
167
+
168
+ def extract_markdown_link_reference_defintions(head, tail)
169
+ [].tap do |definitions|
170
+ tail.scan(MKDWN_LINK_REF_REGEX).each do |segments|
171
+ definitions << segments.join if head.include?(segments[0])
172
+ end
173
+ end
174
+ end
175
+
176
+ def endtag_regex_stash(tag_name)
177
+ @endtag_regex_stash ||= {}
178
+ @endtag_regex_stash[tag_name] ||= %r!{%-?\s*end#{tag_name}.*?\s*-?%}!m
179
+ end
180
+
181
+ def liquid_block?(tag_name)
182
+ return false unless tag_name.is_a?(String)
183
+ return false unless Liquid::Template.tags[tag_name]
184
+
185
+ Liquid::Template.tags[tag_name].ancestors.include?(Liquid::Block)
186
+ rescue NoMethodError
187
+ Bridgetown.logger.error "Error:", "A Liquid tag in the excerpt of" \
188
+ " #{doc.relative_path} couldn't be parsed."
189
+ raise
190
+ end
191
+
192
+ def print_build_warning
193
+ Bridgetown.logger.warn "Warning:", "Excerpt modified in #{doc.relative_path}!"
194
+ Bridgetown.logger.warn "", "Found a Liquid block containing the excerpt separator" \
195
+ " #{doc.excerpt_separator.inspect}. "
196
+ Bridgetown.logger.warn "", "The block has been modified with the appropriate closing tag."
197
+ Bridgetown.logger.warn "", "Feel free to define a custom excerpt or excerpt_separator in the"
198
+ Bridgetown.logger.warn "", "document's Front Matter if the generated excerpt is" \
199
+ " unsatisfactory."
200
+ end
201
+ end
202
+ end