frontman-ssg 0.0.2

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 (141) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +42 -0
  3. data/.github/CODE_OF_CONDUCT.md +9 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  6. data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  7. data/.gitignore +5 -0
  8. data/.rubocop.yml +88 -0
  9. data/CHANGELOG.md +11 -0
  10. data/CONTRIBUTING.md +42 -0
  11. data/Gemfile +5 -0
  12. data/LICENSE.md +21 -0
  13. data/Rakefile +94 -0
  14. data/SECURITY.md +6 -0
  15. data/bin/frontman +6 -0
  16. data/frontman-ssg.gemspec +48 -0
  17. data/frontman.svg +2 -0
  18. data/lib/frontman.rb +15 -0
  19. data/lib/frontman/app.rb +175 -0
  20. data/lib/frontman/bootstrapper.rb +70 -0
  21. data/lib/frontman/builder/asset_pipeline.rb +55 -0
  22. data/lib/frontman/builder/builder.rb +193 -0
  23. data/lib/frontman/builder/file.rb +55 -0
  24. data/lib/frontman/builder/mapping.rb +54 -0
  25. data/lib/frontman/builder/statistics_collector.rb +37 -0
  26. data/lib/frontman/cli.rb +6 -0
  27. data/lib/frontman/commands/build.rb +76 -0
  28. data/lib/frontman/commands/init.rb +58 -0
  29. data/lib/frontman/commands/serve.rb +110 -0
  30. data/lib/frontman/concerns/dispatch_events.rb +56 -0
  31. data/lib/frontman/concerns/forward_calls_to_app.rb +28 -0
  32. data/lib/frontman/config.rb +52 -0
  33. data/lib/frontman/context.rb +125 -0
  34. data/lib/frontman/custom_struct.rb +44 -0
  35. data/lib/frontman/data_store.rb +106 -0
  36. data/lib/frontman/data_store_file.rb +60 -0
  37. data/lib/frontman/helpers/app_helper.rb +18 -0
  38. data/lib/frontman/helpers/link_helper.rb +35 -0
  39. data/lib/frontman/helpers/render_helper.rb +76 -0
  40. data/lib/frontman/helpers/url_helper.rb +11 -0
  41. data/lib/frontman/iterator.rb +48 -0
  42. data/lib/frontman/process/chain.rb +43 -0
  43. data/lib/frontman/process/processor.rb +11 -0
  44. data/lib/frontman/renderers/erb_renderer.rb +21 -0
  45. data/lib/frontman/renderers/haml_renderer.rb +22 -0
  46. data/lib/frontman/renderers/markdown_renderer.rb +26 -0
  47. data/lib/frontman/renderers/renderer.rb +26 -0
  48. data/lib/frontman/renderers/renderer_resolver.rb +26 -0
  49. data/lib/frontman/resource.rb +279 -0
  50. data/lib/frontman/sitemap_tree.rb +211 -0
  51. data/lib/frontman/toolbox/timer.rb +49 -0
  52. data/lib/frontman/version.rb +6 -0
  53. data/project-templates/default/.gitignore +2 -0
  54. data/project-templates/default/Gemfile +3 -0
  55. data/project-templates/default/config.rb +17 -0
  56. data/project-templates/default/data/site.yml +4 -0
  57. data/project-templates/default/helpers/site_helper.rb +7 -0
  58. data/project-templates/default/public/code.css +77 -0
  59. data/project-templates/default/public/frontman-logo.svg +2 -0
  60. data/project-templates/default/public/main.css +27 -0
  61. data/project-templates/default/public/main.js +1 -0
  62. data/project-templates/default/source/index.html.md.erb +7 -0
  63. data/project-templates/default/source/sitemap.xml.erb +11 -0
  64. data/project-templates/default/views/layouts/main.erb +19 -0
  65. data/project-templates/default/views/layouts/main.haml +15 -0
  66. data/project-templates/default/views/partials/menu.erb +7 -0
  67. data/project-templates/webpack/.gitignore +4 -0
  68. data/project-templates/webpack/Gemfile +3 -0
  69. data/project-templates/webpack/README.md +54 -0
  70. data/project-templates/webpack/assets/css/code.css +77 -0
  71. data/project-templates/webpack/assets/css/style.css +27 -0
  72. data/project-templates/webpack/assets/images/.gitkeep +0 -0
  73. data/project-templates/webpack/assets/images/frontman_logo.svg +2 -0
  74. data/project-templates/webpack/assets/js/index.js +1 -0
  75. data/project-templates/webpack/config.rb +24 -0
  76. data/project-templates/webpack/data/site.yml +4 -0
  77. data/project-templates/webpack/helpers/assets_helper.rb +24 -0
  78. data/project-templates/webpack/helpers/site_helper.rb +7 -0
  79. data/project-templates/webpack/package-lock.json +7603 -0
  80. data/project-templates/webpack/package.json +34 -0
  81. data/project-templates/webpack/source/index.html.md.erb +7 -0
  82. data/project-templates/webpack/source/sitemap.xml.erb +11 -0
  83. data/project-templates/webpack/views/layouts/main.erb +20 -0
  84. data/project-templates/webpack/views/layouts/main.haml +14 -0
  85. data/project-templates/webpack/views/partials/menu.erb +7 -0
  86. data/project-templates/webpack/views/partials/script_with_vendors.haml +5 -0
  87. data/project-templates/webpack/webpack/base.config.js +51 -0
  88. data/project-templates/webpack/webpack/dev.config.js +6 -0
  89. data/project-templates/webpack/webpack/prod.config.js +30 -0
  90. data/readme.md +80 -0
  91. data/sorbet/config +2 -0
  92. data/sorbet/rbi/hidden-definitions/errors.txt +27259 -0
  93. data/sorbet/rbi/hidden-definitions/hidden.rbi +45122 -0
  94. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +276 -0
  95. data/sorbet/rbi/todo.rbi +6 -0
  96. data/spec/frontman/app_spec.rb +48 -0
  97. data/spec/frontman/bootstrapper_spec.rb +26 -0
  98. data/spec/frontman/builder/builder_spec.rb +79 -0
  99. data/spec/frontman/builder/file_spec.rb +45 -0
  100. data/spec/frontman/builder/mapping_spec.rb +8 -0
  101. data/spec/frontman/concerns/dispatch_events_spec.rb +70 -0
  102. data/spec/frontman/concerns/forward_calls_to_app_spec.rb +21 -0
  103. data/spec/frontman/config_spec.rb +54 -0
  104. data/spec/frontman/context_spec.rb +48 -0
  105. data/spec/frontman/custom_struct_spec.rb +51 -0
  106. data/spec/frontman/data_store_file_spec.rb +9 -0
  107. data/spec/frontman/data_store_spec.rb +36 -0
  108. data/spec/frontman/frontman_ssg_spec.rb +7 -0
  109. data/spec/frontman/helpers/app_helper_spec.rb +24 -0
  110. data/spec/frontman/helpers/link_helper_spec.rb +37 -0
  111. data/spec/frontman/helpers/render_helper_spec.rb +55 -0
  112. data/spec/frontman/helpers/url_helper_spec.rb +21 -0
  113. data/spec/frontman/iterator_spec.rb +47 -0
  114. data/spec/frontman/mocks/asset.css +3 -0
  115. data/spec/frontman/mocks/config.rb +0 -0
  116. data/spec/frontman/mocks/helpers/formatting_helper.rb +5 -0
  117. data/spec/frontman/mocks/helpers/language_helper.rb +5 -0
  118. data/spec/frontman/mocks/helpers/link_helper.rb +5 -0
  119. data/spec/frontman/mocks/helpers/test_command.rb +5 -0
  120. data/spec/frontman/mocks/html_file.html +8 -0
  121. data/spec/frontman/mocks/html_file.html.md.erb +9 -0
  122. data/spec/frontman/mocks/html_file.md.html +8 -0
  123. data/spec/frontman/mocks/info.yml +4 -0
  124. data/spec/frontman/mocks/layouts/raw_without_body.haml +1 -0
  125. data/spec/frontman/mocks/nested/data.yml +4 -0
  126. data/spec/frontman/mocks/nested/more_data.yml +4 -0
  127. data/spec/frontman/mocks/partials/paragraph.haml +2 -0
  128. data/spec/frontman/mocks/snippet/html_file.html +8 -0
  129. data/spec/frontman/mocks/snippet/html_file.yml +670 -0
  130. data/spec/frontman/mocks/test.html +8 -0
  131. data/spec/frontman/mocks/wrap.haml +3 -0
  132. data/spec/frontman/process/chain_spec.rb +56 -0
  133. data/spec/frontman/renderers/erb_renderer_spec.rb +22 -0
  134. data/spec/frontman/renderers/haml_renderer_spec.rb +12 -0
  135. data/spec/frontman/renderers/markdown_renderer_spec.rb +12 -0
  136. data/spec/frontman/renderers/renderer_spec.rb +16 -0
  137. data/spec/frontman/resource_spec.rb +151 -0
  138. data/spec/frontman/sitemap_tree_spec.rb +128 -0
  139. data/spec/frontman/toolbox/timer_spec.rb +34 -0
  140. data/spec/spec_setup.rb +19 -0
  141. metadata +507 -0
@@ -0,0 +1,76 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'frontman/config'
5
+ require 'frontman/context'
6
+ require 'frontman/renderers/markdown_renderer'
7
+ require 'frontman/renderers/erb_renderer'
8
+ require 'frontman/resource'
9
+ require 'sorbet-runtime'
10
+
11
+ module RenderHelper
12
+ extend T::Sig
13
+
14
+ sig do
15
+ params(template: String, data: T.any(Hash, CustomStruct))
16
+ .returns(String)
17
+ end
18
+ def partial(template, data = {})
19
+ partial_dir = Frontman::Config.get(
20
+ :partial_dir, fallback: 'views/partials'
21
+ )
22
+ r = Frontman::Resource.from_path(
23
+ File.join(partial_dir, template), nil, false
24
+ )
25
+ r.render(nil, data)
26
+ end
27
+
28
+ sig do
29
+ params(
30
+ page: Frontman::Resource, options: T.any(Hash, CustomStruct)
31
+ ).returns(String)
32
+ end
33
+ def render_page(page, options = {})
34
+ # We force not to render any layout
35
+ options[:layout] = nil
36
+ options[:ignore_page] = true
37
+
38
+ # We don't need to cache here since it already done in the render function
39
+ # of the resource
40
+ page.render(nil, options)
41
+ end
42
+
43
+ sig do
44
+ params(options: T.any(Hash, CustomStruct)).returns(String)
45
+ end
46
+ def render_current_page(options = {})
47
+ render_page(Frontman::App.instance.current_page, options)
48
+ end
49
+
50
+ sig { params(content: String).returns(String) }
51
+ def render_markdown(content)
52
+ compiled = Frontman::MarkdownRenderer.instance.compile(content)
53
+ Frontman::MarkdownRenderer.instance.render(
54
+ compiled, nil, Frontman::Context.new, {}
55
+ )
56
+ end
57
+
58
+ sig do
59
+ params(
60
+ content: String,
61
+ template_data: T.nilable(Hash)
62
+ ).returns(String)
63
+ end
64
+ def render_erb(content, template_data = nil)
65
+ context = Frontman::Context.new
66
+
67
+ if !template_data.nil? && template_data
68
+ template_data.each do |key, value|
69
+ context.singleton_class.send(:define_method, key) { value }
70
+ end
71
+ end
72
+
73
+ compiled = Frontman::ErbRenderer.instance.compile(content)
74
+ Frontman::ErbRenderer.instance.render(compiled, nil, context, {})
75
+ end
76
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UrlHelper
4
+ def format_url(url)
5
+ formatted = url.gsub('index.html', '')
6
+ .sub(%r{^/}, '')
7
+ .chomp('/')
8
+
9
+ "/#{formatted}/".gsub('//', '/')
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'parallel'
5
+
6
+ module Frontman
7
+ class Iterator
8
+ class << self
9
+ extend T::Sig
10
+
11
+ def map(collection, *options, &block)
12
+ forward(:map, collection, *options, &block)
13
+ end
14
+
15
+ def each(collection, *options, &block)
16
+ forward(:each, collection, *options, &block)
17
+ end
18
+
19
+ def map_with_index(collection, *options, &block)
20
+ forward(:map_with_index, collection, *options, &block)
21
+ end
22
+
23
+ def each_with_index(collection, *options, &block)
24
+ forward(:each_with_index, collection, *options, &block)
25
+ end
26
+
27
+ def processor_count
28
+ return Parallel.processor_count if parallel?
29
+
30
+ Frontman::Config.get(:processor_count, fallback: 1)
31
+ end
32
+
33
+ private
34
+
35
+ def parallel?
36
+ Frontman::Config.get(:parallel, fallback: true)
37
+ end
38
+
39
+ def forward(method, collection, *options, &block)
40
+ if parallel?
41
+ return ::Parallel.public_send(method, collection, *options, &block)
42
+ end
43
+
44
+ collection.public_send(method, &block)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+
6
+ module Frontman
7
+ module Process
8
+ class Chain
9
+ extend T::Sig
10
+
11
+ sig do
12
+ params(
13
+ processors: T::Array[Frontman::Process::Processor]
14
+ ).returns(T.self_type)
15
+ end
16
+ def initialize(processors = [])
17
+ @processors = []
18
+
19
+ add_processors(processors)
20
+ end
21
+
22
+ sig do
23
+ params(
24
+ processors: T.any(
25
+ Frontman::Process::Processor,
26
+ T::Array[Frontman::Process::Processor]
27
+ )
28
+ ).returns(T.self_type)
29
+ end
30
+ def add_processors(processors)
31
+ @processors.push(*Array(processors))
32
+ self
33
+ end
34
+
35
+ sig { params(arguments: T.untyped).returns(T::Array[T.untyped]) }
36
+ def process(*arguments)
37
+ @processors.map do |processor|
38
+ processor.process(*arguments)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Frontman
4
+ module Process
5
+ class Processor
6
+ def process(*_args)
7
+ raise NoMethodError 'process method is not implemented'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'erubis'
5
+ require 'frontman/renderers/renderer'
6
+
7
+ module Frontman
8
+ class ErbRenderer < Frontman::Renderer
9
+ def compile(layout)
10
+ Erubis::Eruby.new(layout, bufvar: '@_erbout')
11
+ end
12
+
13
+ def render_content(compiled, content, scope, data)
14
+ data.each do |key, value|
15
+ scope.singleton_class.send(:define_method, key) { value }
16
+ end
17
+
18
+ compiled.result(scope.get_binding { content })
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'haml'
5
+ require 'frontman/renderers/renderer'
6
+
7
+ module Frontman
8
+ class HamlRenderer < Renderer
9
+ def initialize
10
+ Haml::Options.defaults[:format] = :html5
11
+ super
12
+ end
13
+
14
+ def compile(layout)
15
+ Haml::Engine.new(layout)
16
+ end
17
+
18
+ def render_content(compiled, content, scope, _data)
19
+ compiled.render(scope.get_binding) { content }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'kramdown'
5
+ require 'frontman/renderers/renderer'
6
+
7
+ module Frontman
8
+ class MarkdownRenderer < Frontman::Renderer
9
+ def compile(layout)
10
+ Kramdown::Document.new(
11
+ layout,
12
+ syntax_highlighter: 'rouge',
13
+ parse_block_html: true,
14
+ fenced_code_blocks: true,
15
+ input: 'GFM',
16
+ with_toc_data: true,
17
+ smartypants: true,
18
+ hard_wrap: false
19
+ )
20
+ end
21
+
22
+ def render_content(compiled, _content, _scope, _data)
23
+ compiled.to_html
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'singleton'
5
+
6
+ module Frontman
7
+ class Renderer
8
+ include Singleton
9
+
10
+ def compile(_layout)
11
+ raise('Should be implemented in child classes')
12
+ end
13
+
14
+ def render(compiled, content, scope, data)
15
+ Frontman::App.instance.view_data.push(data)
16
+ content = render_content(compiled, content, scope, data)
17
+ Frontman::App.instance.view_data.pop
18
+
19
+ content
20
+ end
21
+
22
+ def render_content(_compiled, _content, _scope, _data)
23
+ raise('Should be implemented in child classes')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'frontman/renderers/erb_renderer'
5
+ require 'frontman/renderers/haml_renderer'
6
+ require 'frontman/renderers/markdown_renderer'
7
+ require 'singleton'
8
+ require 'sorbet-runtime'
9
+
10
+ module Frontman
11
+ class RendererResolver
12
+ extend T::Sig
13
+ include Singleton
14
+
15
+ sig { params(extension: String).returns(T.nilable(Frontman::Renderer)) }
16
+ def get_renderer(extension)
17
+ renderers = {
18
+ 'erb': Frontman::ErbRenderer.instance,
19
+ 'md': Frontman::MarkdownRenderer.instance,
20
+ 'haml': Frontman::HamlRenderer.instance
21
+ }
22
+
23
+ renderers[extension.to_sym]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,279 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require 'frontman/renderers/renderer_resolver'
5
+ require 'frontman/custom_struct'
6
+ require 'frontman/context'
7
+ require 'frontman/app'
8
+ require 'frontman/sitemap_tree'
9
+ require 'sorbet-runtime'
10
+ require 'yaml/front-matter'
11
+
12
+ module Frontman
13
+ class Resource
14
+ extend T::Sig
15
+
16
+ attr_reader :dir, :file, :extension, :path
17
+ attr_accessor :destination_path, :data, :renderers, :compiled, :file_path
18
+
19
+ class << self
20
+ extend T::Sig
21
+
22
+ sig { returns(T::Hash[String, Resource]) }
23
+ def resources
24
+ @@resources || {}
25
+ end
26
+
27
+ sig do
28
+ params(
29
+ file_path: String,
30
+ destination_path: T.nilable(String),
31
+ is_page: T::Boolean
32
+ ).returns(Resource)
33
+ end
34
+ def from_path(file_path, destination_path = nil, is_page = true)
35
+ destination_path ||= file_path
36
+ file_path = file_path.gsub(%r{^/}, '')
37
+ destination_path = destination_path.gsub(%r{^/}, '')
38
+ .gsub(%r{/[0-9]+?-}, '/')
39
+ .sub(%r{^source/}, '')
40
+
41
+ # We cache the newly created resource so we avoid loosing the cache
42
+ # if from_path is called again with the same file
43
+ # This is especially important for perf in case of layouts and templates
44
+ @@resources ||= {}
45
+ @@resources[destination_path] ||= new(
46
+ file_path, destination_path, is_page
47
+ )
48
+ end
49
+ end
50
+
51
+ sig { params(path: String).returns(Array) }
52
+ def strip_extensions(path)
53
+ split = path.split('/')
54
+ without_extension = T.must(split.last).split('.')[0]
55
+ path_without_extensions = split.first(split.length - 1)
56
+ .push(T.must(without_extension))
57
+ .join('/')
58
+ extensions = T.must(split.last).split('.')[1..-1]
59
+ [path_without_extensions, extensions]
60
+ end
61
+
62
+ sig { void }
63
+ def setup_destination
64
+ destination_without_extension, dest_file_extensions = strip_extensions(
65
+ @destination_path
66
+ )
67
+
68
+ if destination_without_extension == 'index'
69
+ destination_without_extension = ''
70
+ end
71
+
72
+ is_index_page = destination_without_extension.end_with?('/index')
73
+ @extension = dest_file_extensions.first
74
+
75
+ if (@extension == 'html' || extension.nil?) && !is_index_page
76
+ @destination_path = destination_without_extension + '/index.html'
77
+ else
78
+ @destination_path = destination_without_extension + '.' + @extension
79
+ end
80
+ @path = "/#{@destination_path.chomp('index.html')}"
81
+ .gsub('//', '/')
82
+ end
83
+
84
+ sig { void }
85
+ def parse_data_file
86
+ data_file = @file_path_without_extension + '.yml'
87
+ return unless File.exist?(data_file)
88
+
89
+ begin
90
+ data = YAML.safe_load(File.read(data_file)).to_ostruct
91
+ rescue Psych::SyntaxError => e
92
+ raise("#{e} - #{data_file}")
93
+ end
94
+ @data[:yml] = data
95
+ end
96
+
97
+ sig do
98
+ params(
99
+ parse_parent: T::Boolean,
100
+ data: T.any(NilClass, Hash, CustomStruct, OpenStruct)
101
+ ).void
102
+ end
103
+ def parse_resource(parse_parent = false, data = nil)
104
+ @rendered_content = nil
105
+
106
+ @content = File.read(@file_path)
107
+ @data = {}.to_ostruct
108
+
109
+ if %w[erb html haml md txt].include?(@extension)
110
+ @data, @content = YAML::FrontMatter.extract(@content).to_ostruct
111
+ end
112
+
113
+ @data = @data.to_h.merge(data.to_h).to_ostruct unless data.nil?
114
+
115
+ @compiled = nil
116
+
117
+ # If there is only one renderer we can precompile the file because there
118
+ # is no dependency. This is critical to get good performances, especially
119
+ # for templates and layouts that we also considered as Resources
120
+ if @renderers.size == 1
121
+ @renderer = @renderers[0]
122
+ @compiled = @renderer.compile(@content)
123
+ end
124
+
125
+ if parse_parent
126
+ proxy_files = Frontman::SitemapTree.proxy_resources_from_template(
127
+ @file_path
128
+ )
129
+
130
+ if proxy_files.length
131
+ proxy_files.each do |r|
132
+ r.parse_resource(false, r.data) unless r == self
133
+ end
134
+ end
135
+ end
136
+
137
+ # We need to do that after parsing the resource so @data exists
138
+ parse_data_file if @is_page
139
+ end
140
+
141
+ sig do
142
+ params(
143
+ content_for_layout: T.nilable(String),
144
+ extra_data: T.any(Hash, CustomStruct)
145
+ ).returns(String)
146
+ end
147
+ def render(content_for_layout = nil, extra_data = {})
148
+ view_data = data.to_h.merge(extra_data).to_ostruct
149
+ layout_path = layout
150
+ layout_from_extra_data = false
151
+
152
+ layout_from_extra_data = extra_data[:layout] if extra_data.key?(:layout)
153
+ if view_data.layout
154
+ layout_path = File.join(
155
+ Frontman::Config.get(:layout_dir, fallback: 'views/layouts'),
156
+ view_data.layout
157
+ )
158
+ end
159
+
160
+ if @is_page && !view_data[:ignore_page]
161
+ # We store that in App so it can be accessed from any view
162
+ Frontman::App.instance.current_page = self
163
+ Frontman::App.instance.reset_ids_generation
164
+
165
+ sitemap_tree = Frontman::App.instance.sitemap_tree.from_resource(self)
166
+ Frontman::App.instance.current_tree = sitemap_tree
167
+ end
168
+
169
+ # If we have no layout to render and already cache the rendered content
170
+ # we can return it directly
171
+ if layout_from_extra_data.nil? && !@rendered_content.nil?
172
+ return @rendered_content
173
+ end
174
+
175
+ content = @content
176
+
177
+ # We create a new context for each file getting rendered
178
+ context = Frontman::Context.new
179
+
180
+ if @compiled
181
+ # If we manage to compile the template (only 1 renderer)
182
+ # we render from the compiled template
183
+ content = @renderer.render(
184
+ @compiled, content_for_layout, context, view_data
185
+ )
186
+ else
187
+ # We loop over each renderer (infered from extensions of the file)
188
+ # and render. The result of the 1st rendered is passed as a argument of
189
+ # the second renderer and so on until we have the final content
190
+ @renderers.each do |renderer|
191
+ compiled = renderer.compile(content)
192
+ content = renderer.render(
193
+ compiled, content_for_layout, context, view_data
194
+ )
195
+ end
196
+ end
197
+
198
+ # Cache the rendered content to avoid non necessary processing
199
+ # We do this before rendering the layout because it's usually
200
+ # what's common to every rendering
201
+ @rendered_content = content
202
+
203
+ # If a layout is specified we take the rendered content a inject it
204
+ # inside the layout.
205
+ # If layout:nil was specified when triggering the render we don't
206
+ # render the layout and override the layout that can be set in
207
+ # frontmatter or config.rb
208
+ if layout_path && !layout_from_extra_data.nil?
209
+ content = Resource.from_path(layout_path, nil, false).render(content)
210
+ end
211
+
212
+ content
213
+ end
214
+
215
+ sig { returns(T.nilable(String)) }
216
+ def layout
217
+ return nil unless @is_page
218
+
219
+ Frontman::App.instance.layouts.each do |(glob, layout)|
220
+ return layout if File.fnmatch(glob, @destination_path)
221
+ end
222
+
223
+ nil
224
+ end
225
+
226
+ sig { returns(Hash) }
227
+ def content_blocks
228
+ @content_blocks ||= {}
229
+ end
230
+
231
+ sig { returns(Time) }
232
+ def mtime
233
+ @mtime ||= File.mtime(@file_path)
234
+ end
235
+
236
+ sig { returns(String) }
237
+ def inspect
238
+ "Resource: #{@file_path}"
239
+ end
240
+
241
+ sig { returns(T::Boolean) }
242
+ def indexable?
243
+ data.key?(:indexable) ? data[:indexable] : true
244
+ end
245
+
246
+ private
247
+
248
+ sig do
249
+ params(
250
+ file_path: String,
251
+ destination_path: String,
252
+ is_page: T::Boolean
253
+ ).void
254
+ end
255
+ def initialize(file_path, destination_path, is_page)
256
+ raise "File does not exists: #{file_path}" unless File.exist?(file_path)
257
+
258
+ @file_path = file_path
259
+ @destination_path = destination_path
260
+ @is_page = is_page
261
+
262
+ setup_extension_renderers
263
+ setup_destination
264
+
265
+ parse_resource
266
+ end
267
+
268
+ sig { void }
269
+ def setup_extension_renderers
270
+ @file_path_without_extension, rendering_extensions = strip_extensions(
271
+ @file_path
272
+ )
273
+ rendering_extensions.reverse!
274
+ @renderers = rendering_extensions.map do |ext|
275
+ Frontman::RendererResolver.instance.get_renderer(ext)
276
+ end.compact
277
+ end
278
+ end
279
+ end