amber 0.2.6
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.
- data/LICENSE +661 -0
- data/README.md +114 -0
- data/bin/amber +71 -0
- data/lib/amber.rb +76 -0
- data/lib/amber/cli.rb +98 -0
- data/lib/amber/logger.rb +21 -0
- data/lib/amber/menu.rb +141 -0
- data/lib/amber/page_array.rb +61 -0
- data/lib/amber/render/asset.rb +55 -0
- data/lib/amber/render/autolink.rb +78 -0
- data/lib/amber/render/bracketlink.rb +33 -0
- data/lib/amber/render/helpers/haml_helper.rb +54 -0
- data/lib/amber/render/helpers/html_helper.rb +75 -0
- data/lib/amber/render/helpers/language_helper.rb +25 -0
- data/lib/amber/render/helpers/navigation_helper.rb +203 -0
- data/lib/amber/render/layout.rb +66 -0
- data/lib/amber/render/table_of_contents.rb +383 -0
- data/lib/amber/render/template.rb +158 -0
- data/lib/amber/render/view.rb +189 -0
- data/lib/amber/server.rb +113 -0
- data/lib/amber/site.rb +154 -0
- data/lib/amber/site_configuration.rb +132 -0
- data/lib/amber/static_page.rb +151 -0
- data/lib/amber/static_page/filesystem.rb +270 -0
- data/lib/amber/static_page/page_properties.rb +124 -0
- data/lib/amber/static_page/property_set.rb +78 -0
- data/lib/amber/static_page/render.rb +122 -0
- metadata +203 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
#
|
2
|
+
# Holds many PropertySets for a StaticPage, one PropertySet per locale.
|
3
|
+
#
|
4
|
+
# setting the property (in en.haml):
|
5
|
+
#
|
6
|
+
# - @title = 'hi'
|
7
|
+
#
|
8
|
+
# getting the property
|
9
|
+
#
|
10
|
+
# page.props.title # Uses I18n.locale
|
11
|
+
# page.props.prop(:es, :title) # explicitly uses locale :es
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'i18n'
|
15
|
+
require 'time'
|
16
|
+
require 'rubygems'
|
17
|
+
require 'haml'
|
18
|
+
require 'RedCloth'
|
19
|
+
|
20
|
+
module Amber
|
21
|
+
class StaticPage
|
22
|
+
class PageProperties
|
23
|
+
|
24
|
+
def initialize(page=nil)
|
25
|
+
@page = page
|
26
|
+
@locales = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# evaluate the template_string, and load the variables defined into an AttrObject.
|
31
|
+
#
|
32
|
+
def eval(template_string, locale=I18n.default_locale)
|
33
|
+
locale = locale.to_sym # locales are always symbols
|
34
|
+
|
35
|
+
# render to the template to get the instance variables
|
36
|
+
ps = PropertySet.new
|
37
|
+
begin
|
38
|
+
# template is evaluated with binding of ps object
|
39
|
+
Haml::Engine.new(template_string, :format => :html5).render(ps)
|
40
|
+
rescue Exception => exc
|
41
|
+
raise exc if defined?(TESTING)
|
42
|
+
end
|
43
|
+
|
44
|
+
# convert date/time variables to objects of class Time
|
45
|
+
ps.instance_variables.grep(/_at$/).each do |time_variable|
|
46
|
+
ps.instance_variable_set(time_variable, Time.parse(ps.instance_variable_get(time_variable)))
|
47
|
+
end
|
48
|
+
|
49
|
+
# save the AttrObject
|
50
|
+
@locales[locale] = ps
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# allows property_set.propname shortcut, assumes default locale
|
55
|
+
#
|
56
|
+
def method_missing(method)
|
57
|
+
prop(I18n.locale, method)
|
58
|
+
end
|
59
|
+
|
60
|
+
#def locale(l)
|
61
|
+
# @locales[l.to_sym] || @locales[I18n.default_locale]
|
62
|
+
#end
|
63
|
+
|
64
|
+
#
|
65
|
+
# get an attribute value for a particular locale.
|
66
|
+
# if `inherited` is true, we do not consider special non-inheritable properties.
|
67
|
+
#
|
68
|
+
def prop(locale, var_name, inherited=false)
|
69
|
+
return nil unless locale
|
70
|
+
properties = @locales[locale.to_sym]
|
71
|
+
value = (properties.get(var_name, inherited) if properties)
|
72
|
+
if value.nil? && locale != I18n.default_locale
|
73
|
+
properties = @locales[I18n.default_locale]
|
74
|
+
value = properties.get(var_name, inherited) if properties
|
75
|
+
end
|
76
|
+
if value.nil? && @page && @page.parent
|
77
|
+
value = @page.parent.prop(locale, var_name, true)
|
78
|
+
end
|
79
|
+
value
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# like prop(), but does not allow inheritance
|
84
|
+
#
|
85
|
+
def prop_without_inheritance(locale, var_name)
|
86
|
+
properties = @locales[locale.to_sym]
|
87
|
+
if properties
|
88
|
+
properties.get(var_name)
|
89
|
+
else
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# like prop_without_inheritance, but defaults to default_locale and tries multiple properties
|
96
|
+
#
|
97
|
+
def prop_with_fallback(locale, var_names)
|
98
|
+
[locale, I18n.default_locale].each do |l|
|
99
|
+
var_names.each do |var|
|
100
|
+
value = prop_without_inheritance(l, var)
|
101
|
+
return value if value
|
102
|
+
end
|
103
|
+
end
|
104
|
+
return nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_prop(locale, var_name, value)
|
108
|
+
properties = @locales[locale.to_sym]
|
109
|
+
if properties
|
110
|
+
properties.set(var_name, value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# tries to get the value of an inherited variable
|
116
|
+
#
|
117
|
+
#def get_inherited_var(var_name, locale=I18n.locale)
|
118
|
+
# if @page && @page.parent && @page.parent.props
|
119
|
+
# @page.parent.props.get_var(var_name, locale)
|
120
|
+
# end
|
121
|
+
#end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#
|
2
|
+
# A simple class to hold a set of properties for a page.
|
3
|
+
#
|
4
|
+
# There is a separate property set for each locale. The PageProperties object holds many PropertySets, one
|
5
|
+
# for each locale.
|
6
|
+
#
|
7
|
+
# When the template for a page is evaluated, all the member variabled defined in that template
|
8
|
+
# are loaded as member variables of the PropertySet instance. (e.g. properties are eval'ed
|
9
|
+
# in context of PropertySet instance)
|
10
|
+
#
|
11
|
+
# the "@this" variable is to set variables that should not be inherited
|
12
|
+
#
|
13
|
+
|
14
|
+
module Amber
|
15
|
+
class StaticPage
|
16
|
+
class PropertySet
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@this = ThisPropertySet.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(method, *args)
|
23
|
+
if method =~ /=$/
|
24
|
+
set(method, args.first)
|
25
|
+
else
|
26
|
+
get(method)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def textile(str)
|
31
|
+
RedCloth.new(str).to_html
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# get the value of a property
|
36
|
+
#
|
37
|
+
# the @this properties are non-inheritable. If `inheritable_only` is true, we don't consider them
|
38
|
+
# when returning the property value.
|
39
|
+
#
|
40
|
+
def get(property_name, inheritable_only=false)
|
41
|
+
if inheritable_only || @this.nil?
|
42
|
+
instance_variable_get("@#{property_name}")
|
43
|
+
else
|
44
|
+
value = @this.get(property_name)
|
45
|
+
if value.nil?
|
46
|
+
value = instance_variable_get("@#{property_name}")
|
47
|
+
end
|
48
|
+
value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# set the value of a property
|
54
|
+
#
|
55
|
+
# if the property has a non-nil value set in the @this prop set, then we set it there.
|
56
|
+
# otherwise, it is set in the inheritable set.
|
57
|
+
#
|
58
|
+
def set(property_name, value)
|
59
|
+
property_name = property_name.to_s.sub(/=$/, '')
|
60
|
+
instance_variable = "@" + property_name
|
61
|
+
if @this.nil? || @this.get(property_name).nil?
|
62
|
+
instance_variable_set(instance_variable, value)
|
63
|
+
else
|
64
|
+
@this.instance_variable_set(instance_variable, value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
"<" + instance_variables.map{|v| "#{v}=#{instance_variable_get(v)}"}.join(', ') + ">"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ThisPropertySet < PropertySet
|
74
|
+
def initialize
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#
|
2
|
+
# All the StaticPage code that deals with rendering
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'i18n'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
module Amber
|
10
|
+
class StaticPage
|
11
|
+
|
12
|
+
#
|
13
|
+
# render without layout, possibly with via a rails request
|
14
|
+
#
|
15
|
+
# RAILS
|
16
|
+
# def render_to_string(renderer=nil)
|
17
|
+
# begin
|
18
|
+
# render_locale(renderer, I18n.locale)
|
19
|
+
# rescue ActionView::MissingTemplate, MissingTemplate => exc
|
20
|
+
# begin
|
21
|
+
# render_locale(renderer, I18n.default_locale)
|
22
|
+
# rescue
|
23
|
+
# Amber.logger.error "ERROR: could not file template path #{self.template_path}"
|
24
|
+
# raise exc
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
|
29
|
+
#
|
30
|
+
# render a static copy
|
31
|
+
#
|
32
|
+
# dest_dir - e.g. amber_root/public/
|
33
|
+
#
|
34
|
+
def render_to_file(dest_dir, options={})
|
35
|
+
render_content_files(dest_dir, options)
|
36
|
+
render_assets(dest_dir)
|
37
|
+
if aliases.any?
|
38
|
+
link_page_aliases(dest_dir)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# RAILS
|
45
|
+
# def render_locale(renderer, locale)
|
46
|
+
# if renderer && is_haml_template?(locale)
|
47
|
+
# renderer.render_to_string(:template => self.template_path(locale), :layout => false).html_safe
|
48
|
+
# else
|
49
|
+
# render_static_locale(locale).html_safe
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
|
53
|
+
# RAILS
|
54
|
+
# def render_static_locale(locale)
|
55
|
+
# content_files.each do |file_locale, content_file|
|
56
|
+
# if locale == file_locale
|
57
|
+
# return Render::View.new(self, self.mount_point).render({file: content_file}, {locale: file_locale})
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
# raise MissingTemplate.new(template_path(locale))
|
61
|
+
# end
|
62
|
+
|
63
|
+
# called only by render_to_file
|
64
|
+
def render_assets(dest_dir)
|
65
|
+
asset_files.each do |asset_file|
|
66
|
+
src_file = File.join(@file_path, asset_file)
|
67
|
+
dst_file = File.join(dest_dir, *@path, asset_file)
|
68
|
+
Render::Asset.render(src_file, dst_file)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# called only by render_to_file
|
73
|
+
# for now, we only support aliases on pages that are directories, for simplicity sake.
|
74
|
+
def link_page_aliases(dest_dir)
|
75
|
+
if @simple_page
|
76
|
+
Amber.logger.warn { "The page `#{@file_path}` sets a path alias, but currently aliases are only supported for directory-based pages. Skipping." }
|
77
|
+
else
|
78
|
+
aliases.each do |alias_path|
|
79
|
+
alias_path = Pathname.new(File.join(dest_dir, alias_path))
|
80
|
+
if !File.directory?(File.dirname(alias_path))
|
81
|
+
Amber.logger.warn { "On page `#{@file_path}`, the parent directories for alias name `#{alias_path}` don't exist. Skipping alias." }
|
82
|
+
else
|
83
|
+
page_path = Pathname.new(File.join(dest_dir, *@path))
|
84
|
+
target = page_path.relative_path_from(alias_path).to_s.sub(/^\.\.\//, '')
|
85
|
+
if File.exists?(alias_path)
|
86
|
+
if File.symlink?(alias_path)
|
87
|
+
File.unlink(alias_path)
|
88
|
+
Amber.logger.debug { "Alias #{alias_path} => #{target}" }
|
89
|
+
FileUtils.ln_s(target, alias_path)
|
90
|
+
else
|
91
|
+
Amber.logger.warn { "The page `#{@file_path}` sets a path alias, but there is already a file in the way (`#{alias_path}`)" }
|
92
|
+
end
|
93
|
+
else
|
94
|
+
Amber.logger.debug { "Alias #{alias_path} => #{target}" }
|
95
|
+
FileUtils.ln_s(target, alias_path)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# called only by render_to_file
|
103
|
+
def render_content_files(dest_dir, options)
|
104
|
+
view = Render::View.new(self, self.mount_point)
|
105
|
+
@mount_point.locales.each do |file_locale|
|
106
|
+
content_file = content_file(file_locale)
|
107
|
+
next unless content_file
|
108
|
+
dest = destination_file(dest_dir, file_locale)
|
109
|
+
unless Dir.exists?(File.dirname(dest))
|
110
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
111
|
+
end
|
112
|
+
if options[:force] || !File.exist?(dest) || File.mtime(content_file) > File.mtime(dest)
|
113
|
+
File.open(dest, 'w') do |f|
|
114
|
+
layout = @props.layout || 'default'
|
115
|
+
f.write view.render({file: content_file, layout: layout}, {locale: file_locale})
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: amber
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.6
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Elijah Sparrow
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-03-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: i18n
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: haml
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: haml-contrib
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: RedCloth
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rdiscount
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: nokogiri
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.6.1
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.6.1
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: tilt
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.0.0
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.0.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: sass
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '3.2'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '3.2'
|
142
|
+
description: Amber is a super simple and super flexible static website generator with
|
143
|
+
support for nice localization and navigation.
|
144
|
+
email:
|
145
|
+
- elijah@riseup.net
|
146
|
+
executables:
|
147
|
+
- amber
|
148
|
+
extensions: []
|
149
|
+
extra_rdoc_files: []
|
150
|
+
files:
|
151
|
+
- lib/amber.rb
|
152
|
+
- lib/amber/cli.rb
|
153
|
+
- lib/amber/site_configuration.rb
|
154
|
+
- lib/amber/static_page/render.rb
|
155
|
+
- lib/amber/static_page/page_properties.rb
|
156
|
+
- lib/amber/static_page/property_set.rb
|
157
|
+
- lib/amber/static_page/filesystem.rb
|
158
|
+
- lib/amber/server.rb
|
159
|
+
- lib/amber/site.rb
|
160
|
+
- lib/amber/menu.rb
|
161
|
+
- lib/amber/logger.rb
|
162
|
+
- lib/amber/render/table_of_contents.rb
|
163
|
+
- lib/amber/render/view.rb
|
164
|
+
- lib/amber/render/bracketlink.rb
|
165
|
+
- lib/amber/render/helpers/language_helper.rb
|
166
|
+
- lib/amber/render/helpers/navigation_helper.rb
|
167
|
+
- lib/amber/render/helpers/haml_helper.rb
|
168
|
+
- lib/amber/render/helpers/html_helper.rb
|
169
|
+
- lib/amber/render/autolink.rb
|
170
|
+
- lib/amber/render/asset.rb
|
171
|
+
- lib/amber/render/layout.rb
|
172
|
+
- lib/amber/render/template.rb
|
173
|
+
- lib/amber/static_page.rb
|
174
|
+
- lib/amber/page_array.rb
|
175
|
+
- bin/amber
|
176
|
+
- LICENSE
|
177
|
+
- README.md
|
178
|
+
homepage: https://github.com/leapcode/amber
|
179
|
+
licenses:
|
180
|
+
- AGPL
|
181
|
+
post_install_message:
|
182
|
+
rdoc_options: []
|
183
|
+
require_paths:
|
184
|
+
- lib
|
185
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
186
|
+
none: false
|
187
|
+
requirements:
|
188
|
+
- - ! '>='
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '1.9'
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
none: false
|
193
|
+
requirements:
|
194
|
+
- - ! '>='
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
requirements: []
|
198
|
+
rubyforge_project:
|
199
|
+
rubygems_version: 1.8.23
|
200
|
+
signing_key:
|
201
|
+
specification_version: 3
|
202
|
+
summary: Static website generator
|
203
|
+
test_files: []
|