fronde 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/bin/fronde +15 -30
  3. data/lib/ext/nil_time.rb +25 -0
  4. data/lib/ext/r18n.rb +17 -0
  5. data/lib/ext/time.rb +49 -0
  6. data/lib/fronde/cli/commands.rb +92 -103
  7. data/lib/fronde/cli/data/Rakefile +8 -0
  8. data/lib/fronde/cli/data/config.yml +13 -0
  9. data/lib/fronde/cli/data/gitignore +7 -0
  10. data/lib/fronde/cli/data/zsh_completion +37 -0
  11. data/lib/fronde/cli/helpers.rb +55 -0
  12. data/lib/fronde/cli/opt_parse.rb +143 -0
  13. data/lib/fronde/cli/throbber.rb +99 -0
  14. data/lib/fronde/cli.rb +41 -42
  15. data/lib/fronde/config/data/org-config.el +24 -0
  16. data/lib/fronde/config/{ox-fronde.el → data/ox-fronde.el} +1 -1
  17. data/lib/fronde/config/helpers.rb +80 -0
  18. data/lib/fronde/config/lisp.rb +70 -0
  19. data/lib/fronde/config.rb +135 -99
  20. data/lib/fronde/emacs.rb +23 -20
  21. data/lib/fronde/index/atom_generator.rb +55 -66
  22. data/lib/fronde/index/data/all_tags.org +14 -0
  23. data/lib/fronde/index/data/template.org +22 -0
  24. data/lib/fronde/index/data/template.xml +37 -0
  25. data/lib/fronde/index/org_generator.rb +70 -88
  26. data/lib/fronde/index.rb +56 -82
  27. data/lib/fronde/org/file.rb +287 -0
  28. data/lib/fronde/org/file_extracter.rb +98 -0
  29. data/lib/fronde/org.rb +103 -0
  30. data/lib/fronde/preview.rb +43 -39
  31. data/lib/fronde/slug.rb +27 -0
  32. data/lib/fronde/source/gemini.rb +39 -0
  33. data/lib/fronde/source/html.rb +67 -0
  34. data/lib/fronde/source.rb +204 -0
  35. data/lib/fronde/templater.rb +94 -71
  36. data/lib/fronde/version.rb +1 -1
  37. data/lib/tasks/cli.rake +33 -0
  38. data/lib/tasks/org.rake +62 -42
  39. data/lib/tasks/site.rake +68 -30
  40. data/lib/tasks/sync.rake +41 -21
  41. data/lib/tasks/tags.rake +11 -7
  42. data/locales/en.yml +60 -14
  43. data/locales/fr.yml +68 -14
  44. metadata +53 -110
  45. data/lib/fronde/config/lisp_config.rb +0 -340
  46. data/lib/fronde/config/org-config.el +0 -19
  47. data/lib/fronde/org_file/class_methods.rb +0 -72
  48. data/lib/fronde/org_file/extracter.rb +0 -72
  49. data/lib/fronde/org_file/htmlizer.rb +0 -43
  50. data/lib/fronde/org_file.rb +0 -298
  51. data/lib/fronde/utils.rb +0 -229
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fronde
4
+ class Source
5
+ # Specific settings for HTML {Fronde::Source}
6
+ class Html < Source
7
+ def org_config
8
+ config = super
9
+ config[0]['theme'] = @config['theme']
10
+ config
11
+ end
12
+
13
+ class << self
14
+ def org_default_postamble
15
+ <<~POSTAMBLE
16
+ <p><span class="author">#{R18n.t.fronde.org.postamble.written_by}</span>
17
+ #{R18n.t.fronde.org.postamble.with_emacs_html}</p>
18
+ <p class="date">#{R18n.t.fronde.org.postamble.last_modification}</p>
19
+ <p class="validation">%v</p>
20
+ POSTAMBLE
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def fill_in_specific_config
27
+ @config.merge!(
28
+ 'type' => 'html', 'ext' => '.html', 'mime_type' => 'text/html',
29
+ 'folder' => CONFIG.get('html_public_folder')
30
+ )
31
+ end
32
+
33
+ def org_publish_options
34
+ if @config['is_blog']
35
+ @config['atom_feed'] = <<~ATOMFEED
36
+ <link rel="alternate" type="application/atom+xml" title="#{@config['title']}"
37
+ href="#{@config['domain']}#{public_absolute_path}feeds/index.xml" />
38
+ ATOMFEED
39
+ end
40
+ super
41
+ end
42
+
43
+ def org_default_options # rubocop:disable Metrics/MethodLength
44
+ defaults = {
45
+ 'publishing-function' => 'org-html-publish-to-html',
46
+ 'html-head-include-default-style' => 't',
47
+ 'html-head-include-scripts' => 't',
48
+ 'html-head' => '{{ atom_feed }}',
49
+ 'html-postamble' => Html.org_default_postamble
50
+ }
51
+ return defaults if @config['theme'] == 'default'
52
+
53
+ defaults.merge(
54
+ 'html-head-include-default-style' => 'nil',
55
+ 'html-head-include-scripts' => 'nil',
56
+ 'html-head' => <<~HTMLHEAD
57
+ <link rel="stylesheet" type="text/css" media="screen"
58
+ href="{{ domain }}/assets/{{ theme }}/css/style.css">
59
+ <link rel="stylesheet" type="text/css" media="screen"
60
+ href="{{ domain }}/assets/{{ theme }}/css/htmlize.css">
61
+ {{ atom_feed }}
62
+ HTMLHEAD
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fronde
4
+ # Wrapper for each possible project source.
5
+ # Must be subclassed by specific source type (like Gemini or HTML)
6
+ class Source
7
+ def initialize(source_config)
8
+ @config = {
9
+ 'recursive' => true, 'is_blog' => false,
10
+ 'domain' => CONFIG.get('domain'), 'atom_feed' => '',
11
+ 'org-options' => {
12
+ 'section-numbers' => 'nil', 'with-toc' => 'nil'
13
+ }
14
+ }.merge(source_config)
15
+ clean_config
16
+ org_publish_options
17
+ render_heading
18
+ end
19
+
20
+ def [](key)
21
+ @config[key]
22
+ end
23
+
24
+ def []=(key, value)
25
+ @config[key] = value
26
+ end
27
+
28
+ def type
29
+ @config['type']
30
+ end
31
+
32
+ def recursive?
33
+ !!@config['recursive']
34
+ end
35
+
36
+ def blog?
37
+ !!@config['is_blog']
38
+ end
39
+
40
+ def to_h
41
+ @config
42
+ end
43
+
44
+ def source_for?(file_name)
45
+ relative_file_path = file_name.delete_prefix "#{@config['path']}/"
46
+ # file_name does not begin with source path.
47
+ return false if relative_file_path == file_name
48
+
49
+ # Looks like a file at a deeper level, but current source is not
50
+ # recursive.
51
+ return false if relative_file_path.include?('/') && !recursive?
52
+
53
+ # We don’t check file if the file really exist as the current
54
+ # check may be done before the file is actually written.
55
+ true
56
+ end
57
+
58
+ def source_for(file_name)
59
+ relative_file_path = file_name.delete_prefix "#{publication_path}/"
60
+ # file_name does not begin with source path.
61
+ return nil if relative_file_path == file_name
62
+
63
+ # Looks like a file at a deeper level, but current source is not
64
+ # recursive.
65
+ return nil if relative_file_path.include?('/') && !recursive?
66
+
67
+ # Looks like a match. But does a source file for this one actually
68
+ # exists?
69
+ relative_source_path = relative_file_path.sub(
70
+ /#{@config['ext']}\z/, '.org'
71
+ )
72
+ source_path = File.join(@config['path'], relative_source_path)
73
+ return nil unless File.file?(source_path)
74
+
75
+ source_path
76
+ end
77
+
78
+ def target_for(file_name)
79
+ target = File.expand_path file_name
80
+ target.delete_prefix! "#{Dir.pwd}/"
81
+ target.sub!(/\.org\z/, @config['ext'])
82
+ project_relative_path = @config['path'].delete_prefix("#{Dir.pwd}/")
83
+ target.delete_prefix! "#{project_relative_path}/"
84
+ public_absolute_path + target
85
+ end
86
+
87
+ def exclude_file?(file_name)
88
+ # Obviously excluding index itself for blogs
89
+ return true if file_name == File.join(@config['path'], 'index.org')
90
+
91
+ exclusion_rules = @config['exclude']
92
+ return false unless exclusion_rules
93
+
94
+ file_name.match? exclusion_rules
95
+ end
96
+
97
+ def org_config
98
+ name = @config['name']
99
+ [{ 'name' => name, 'attributes' => org_project_config },
100
+ { 'name' => "#{name}-assets", 'attributes' => org_assets_config }]
101
+ end
102
+
103
+ # Return the publication absolute path on file system.
104
+ #
105
+ # The returned string never end with a slash (/).
106
+ #
107
+ # Use {Fronde::Source#public_absolute_path} to get the absolute path
108
+ # of this project, as seen from a web browser.
109
+ #
110
+ # @return [String] the absolute path to the target dir of this project
111
+ def publication_path
112
+ return @config['publication_path'] if @config['publication_path']
113
+
114
+ publish_in = [File.expand_path(@config['folder']), @config['target']]
115
+ @config['publication_path'] = publish_in.join('/').delete_suffix('/')
116
+ end
117
+
118
+ # Return the absolute path as seen in User Agent.
119
+ #
120
+ # The returned string always end with a slash (/).
121
+ #
122
+ # Use {Fronde::Source#publication_path} to locate published file on
123
+ # the file system.
124
+ #
125
+ # @return [String] the absolute path to this project
126
+ def public_absolute_path
127
+ return @config['public_absolute_path'] if @config['public_absolute_path']
128
+
129
+ @config['public_absolute_path'] = "/#{@config['target']}/".squeeze('/')
130
+ end
131
+
132
+ class << self
133
+ def canonical_config(config)
134
+ config = { 'path' => config } if config.is_a?(String)
135
+ config['type'] ||= 'html'
136
+ config
137
+ end
138
+
139
+ def new_from_config(config)
140
+ klass_name = config['type'].capitalize
141
+ klass = Kernel.const_get("::Fronde::Source::#{klass_name}")
142
+ klass.new(config)
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def clean_config
149
+ fill_in_specific_config
150
+ @config['name'] ||= @config['path'].sub(/^[.~]*\//, '').tr('/.', '-')
151
+ @config['title'] ||= @config['path']
152
+ @config['target'] ||= File.basename(@config['path']).delete_prefix '.'
153
+ @config['target'] = '' if @config['target'] == '.'
154
+ @config['path'] = File.expand_path(@config['path'])
155
+ @config['theme'] ||= CONFIG.get('theme', 'default')
156
+ # Blog are necessarily recursive to allow publication of tags and feeds
157
+ @config['recursive'] = true if @config['is_blog']
158
+ end
159
+
160
+ def org_publish_options
161
+ type = @config['type']
162
+ options = org_default_options.merge(
163
+ @config['org-options'],
164
+ CONFIG.get("org-#{type}", {}),
165
+ @config["org-#{type}"] || {}
166
+ )
167
+ @config['org-options'] = options
168
+ end
169
+
170
+ def render_heading
171
+ heading_key = "#{@config['type']}-head"
172
+ heading = @config.dig 'org-options', heading_key
173
+ @config['org-options'][heading_key] = \
174
+ Config::Helpers.render_liquid_template(
175
+ heading, to_h
176
+ )
177
+ end
178
+
179
+ def org_project_config
180
+ attributes = {
181
+ 'base-directory' => @config['path'],
182
+ 'base-extension' => 'org',
183
+ 'publishing-directory' => publication_path,
184
+ 'recursive' => @config['recursive']
185
+ }.merge(@config['org-options'])
186
+ exclude = @config['exclude']
187
+ attributes['exclude'] = exclude if exclude
188
+ attributes.sort.to_h # Have lisp config sorted
189
+ end
190
+
191
+ def org_assets_config
192
+ {
193
+ 'base-directory' => @config['path'],
194
+ 'base-extension' => %w[gif jpg png svg pdf].join('\\\\|'),
195
+ 'publishing-directory' => publication_path,
196
+ 'publishing-function' => 'org-publish-attachment',
197
+ 'recursive' => @config['recursive']
198
+ }
199
+ end
200
+ end
201
+ end
202
+
203
+ require_relative 'source/gemini'
204
+ require_relative 'source/html'
@@ -2,117 +2,140 @@
2
2
 
3
3
  require 'nokogiri'
4
4
  require 'digest/md5'
5
- require 'fronde/org_file'
5
+ require_relative 'org/file'
6
6
 
7
7
  module Fronde
8
+ class NoHeadError < ::StandardError; end
9
+
8
10
  # Insert custom part inside generated HTML files.
9
11
  class Templater
10
- def initialize(source, dom, opts = {})
12
+ def initialize(source, dom, config = {})
11
13
  @dom = dom
12
14
  @org_file = source
13
- @position = opts['type'] || 'after'
14
- @content = extract_content opts
15
- @element = @dom.css(opts['selector'])
16
- digest = Digest::MD5.hexdigest(@content)
17
- @check_line = " Fronde Template: #{digest} "
15
+ @config = { 'type' => 'after' }.merge(config)
16
+ digest = Digest::MD5.hexdigest(config.to_s)
17
+ @config['check_line'] = " Fronde Template: #{digest} "
18
18
  end
19
19
 
20
20
  def apply
21
- flag_head
22
- content = @org_file.format(@content)
23
- @element.each do |e|
24
- insert_new_node_at e, content
21
+ # Flag the file for this template
22
+ head = @dom.xpath('//head').first
23
+ raise NoHeadError, self unless head
24
+
25
+ head.prepend_child("<!--#{@config['check_line']}-->")
26
+ content = @org_file.format extract_content
27
+ # Remove source element if necessary to avoid doubling it during
28
+ # the insert action
29
+ @config['source'].unlink if @config.has_key? 'source'
30
+ # Insert new content
31
+ @dom.css(@config['selector']).each do |element|
32
+ insert_new_node_at element, content
25
33
  end
26
34
  end
27
35
 
28
36
  def in_head?
29
- @dom.xpath('//head').children.to_a.filter(&:comment?).each do |c|
30
- return true if c.text == @check_line
37
+ @dom.xpath('//head').children.any? do |child|
38
+ next false unless child.comment?
39
+
40
+ child.text == @config['check_line']
41
+ end
42
+ end
43
+
44
+ def valid?(file_name)
45
+ return false unless @config.has_key?('selector')
46
+
47
+ unless @config.has_key?('content') || @config.has_key?('source')
48
+ return false
31
49
  end
32
- false
50
+
51
+ check_path(file_name)
33
52
  end
34
53
 
35
54
  class << self
36
- def customize_output(file_name, source = nil)
37
- templates_to_apply = filter_templates(file_name)
38
- return if templates_to_apply.empty?
39
- if source.nil?
40
- sourcepath = Fronde::OrgFile.source_for_target(file_name)
41
- source = Fronde::OrgFile.new(sourcepath)
42
- end
55
+ def customize_output(file_name)
56
+ source = Fronde::Org::File.new(file_name)
57
+ # Return if no org file found for this published file
58
+ return if source.file.end_with?(file_name)
59
+
43
60
  dom = open_dom(file_name)
44
- templates_to_apply.each do |t|
45
- tpl = Fronde::Templater.new(source, dom, t)
46
- next if tpl.in_head?
47
- tpl.apply
48
- end
49
- write_dom(file_name, dom)
61
+ updated = apply_templates(source, dom, file_name)
62
+ write_dom(file_name, dom) if updated
50
63
  end
51
64
 
52
65
  private
53
66
 
54
- def filter_templates(file_name)
55
- templates = Fronde::Config.get('templates')
56
- return [] if templates.nil? || templates.empty?
57
- templates.filter { |t| check_required_keys(t, file_name) }
67
+ def apply_templates(source, dom, file_name)
68
+ Fronde::CONFIG.get('templates', []).map do |config|
69
+ template = Fronde::Templater.new(source, dom, config)
70
+ next if !template.valid?(file_name) || template.in_head?
71
+
72
+ template.apply
73
+ true
74
+ rescue NoHeadError
75
+ warn R18n.t.fronde.error.templater.no_head_element(file: file_name)
76
+ next
77
+ end.any?
58
78
  end
59
79
 
60
80
  def open_dom(file_name)
61
- file = File.new file_name, 'r'
62
- dom = Nokogiri::HTML file
63
- file.close
64
- dom
81
+ File.open(file_name, 'r') do |file|
82
+ Nokogiri::HTML file
83
+ end
65
84
  end
66
85
 
67
86
  def write_dom(file_name, dom)
68
- file = File.new file_name, 'w'
69
- dom.write_to file
70
- file.close
71
- end
72
-
73
- def check_path(file_name, pathes)
74
- pub_folder = Fronde::Config.get('public_folder')
75
- if pathes.is_a?(Array)
76
- pathes.each do |tp|
77
- return true if File.fnmatch?("#{pub_folder}#{tp}",
78
- file_name, File::FNM_DOTMATCH)
79
- end
80
- return false
87
+ File.open(file_name, 'w') do |file|
88
+ dom.write_to file
81
89
  end
82
- File.fnmatch?("#{pub_folder}#{pathes}",
83
- file_name, File::FNM_DOTMATCH)
84
- end
85
-
86
- def check_required_keys(opts, file_name)
87
- return false unless opts.has_key?('selector')
88
- return false unless opts.has_key?('content') || opts.has_key?('source')
89
- return check_path(file_name, opts['path']) if opts.has_key?('path')
90
- true
91
90
  end
92
91
  end
93
92
 
94
93
  private
95
94
 
96
- def flag_head
97
- @dom.xpath('//head').first.prepend_child("<!--#{@check_line}-->\n")
98
- end
99
-
100
- def insert_new_node_at(elem, content)
101
- case @position
95
+ def insert_new_node_at(element, content)
96
+ case @config['type']
102
97
  when 'before'
103
- elem.add_previous_sibling content
98
+ element.add_previous_sibling content
104
99
  when 'replace'
105
- elem.replace content
100
+ element.replace content
106
101
  else
107
- elem.add_next_sibling content
102
+ element.add_next_sibling content
103
+ end
104
+ end
105
+
106
+ def extract_content
107
+ # We must either have a source or a content key
108
+ source = @config.delete 'source'
109
+ unless source.is_a?(String) && source != ''
110
+ return @config['content'] || ''
111
+ end
112
+
113
+ node = @dom.css(source)
114
+ # Do nothing if we don’t have a reliable content to work with
115
+ unless node.any?
116
+ warn(
117
+ R18n.t.fronde.error.templater.no_element_found(
118
+ source: source,
119
+ file: Fronde::CONFIG.get('html_public_folder') + @org_file.pub_file
120
+ )
121
+ )
122
+ return ''
108
123
  end
124
+
125
+ @config['source'] = node
126
+ node.to_s
109
127
  end
110
128
 
111
- def extract_content(opts)
112
- return opts['content'] if opts['content']
113
- # If we don't have a content option, then we must have a source
114
- # one.
115
- @dom.css(opts['source']).unlink.to_s
129
+ def check_path(file_name)
130
+ paths = @config['path']
131
+ return true unless paths
132
+
133
+ paths = [paths] unless paths.is_a? Array
134
+
135
+ pub_folder = Fronde::CONFIG.get('html_public_folder')
136
+ paths.any? do |template_path|
137
+ File.fnmatch?("#{pub_folder}#{template_path}", file_name)
138
+ end
116
139
  end
117
140
  end
118
141
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Fronde
4
4
  # @return [String] the version number of the current Fronde release.
5
- VERSION = '0.3.4'
5
+ VERSION = '0.4.0'
6
6
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../fronde/cli/opt_parse'
4
+
5
+ namespace :cli do
6
+ desc 'Generate an autocomplete file for zsh'
7
+ task :zsh_complete do
8
+ data = {}
9
+ all_commands = Fronde::CLI::OptParse::FRONDE_COMMANDS
10
+ data['commands'] = all_commands.filter_map do |command, options|
11
+ next if options[:alias] || command == 'basic'
12
+
13
+ opts = (options[:opts] || []).map do |opt|
14
+ opt_config = Fronde::CLI::OptParse::FRONDE_OPTIONS[opt]
15
+ keyword = nil
16
+ unless opt_config[:boolean]
17
+ keyword = opt_config[:keyword] || opt_config[:long].upcase
18
+ end
19
+ { 'short' => opt,
20
+ 'long' => "--#{opt_config[:long]}",
21
+ 'keyword' => keyword }
22
+ end
23
+
24
+ translation = R18n.t.fronde.bin.commands[command].tr("'", '’')
25
+ { 'name' => command,
26
+ 'translation' => translation,
27
+ 'options' => opts }
28
+ end
29
+ source = File.expand_path '../fronde/cli/data/zsh_completion', __dir__
30
+ template = Liquid::Template.parse(File.read(source))
31
+ puts template.render(data)
32
+ end
33
+ end
data/lib/tasks/org.rake CHANGED
@@ -2,61 +2,57 @@
2
2
 
3
3
  require 'open-uri'
4
4
 
5
- # Fronde::Config is required by Fronde::Utils
6
- require 'fronde/utils'
5
+ require_relative '../fronde/config'
6
+ require_relative '../fronde/cli/throbber'
7
7
 
8
8
  require 'rake/clean'
9
9
 
10
10
  CLOBBER.push(
11
- 'var/tmp/org.tar.gz', 'var/tmp/last_org_version',
12
- 'var/lib/org-config.el', '.dir-locals.el', 'lib/htmlize.el'
11
+ 'var/lib/org-config.el', '.dir-locals.el',
12
+ 'lib/htmlize.el'
13
13
  )
14
14
 
15
- def make_org_cmd(org_dir, target)
16
- make = ['make', '-C', org_dir, target]
17
- return make.join(' ') if verbose
18
- make.insert(3, '-s')
19
- make << 'EMACSQ="emacs -Q --eval \'(setq inhibit-message t)\'"'
20
- make.join(' ')
21
- end
22
-
23
15
  namespace :org do
24
16
  directory 'var/tmp'
25
17
 
26
18
  desc 'Download last version of Org'
27
19
  file 'var/tmp/org.tar.gz' => 'var/tmp' do
28
- download = Thread.new do
29
- Thread.current[:org_version] = Fronde::Config.org_last_version
30
- Fronde::Utils.download_org
31
- end
20
+ # Weird Rake issue, still executing the task even if the file exists
21
+ next if File.exist? 'var/tmp/org.tar.gz'
22
+
23
+ download = Thread.new { Fronde::Org.download }
32
24
  if verbose
33
- download.join
34
- warn "Org version #{download[:org_version]} has been downloaded"
25
+ warn R18n.t.fronde.tasks.org.downloaded(version: download.value)
35
26
  else
36
- Fronde::Utils.throbber(download, 'Downloading Org:')
27
+ Fronde::CLI::Throbber.run(download, R18n.t.fronde.tasks.org.downloading)
37
28
  end
29
+ rescue RuntimeError
30
+ warn R18n.t.fronde.tasks.org.no_download if verbose
38
31
  end
39
32
 
40
33
  desc 'Compile Org'
41
- task compile: 'var/tmp/org.tar.gz' do |task|
42
- org_version = Fronde::Config.org_last_version
34
+ multitask compile: ['var/tmp/org.tar.gz', 'lib'] do |task|
35
+ begin
36
+ # No need to force fetch last version as it is only interesting as
37
+ # part of the upgrade task
38
+ org_version = Fronde::Org.last_version
39
+ rescue RuntimeError
40
+ next
41
+ end
43
42
  org_dir = "lib/org-#{org_version}"
44
43
  next if Dir.exist?("#{org_dir}/lisp")
44
+
45
45
  build = Thread.new do
46
- sh "tar -C lib -xzf #{task.prerequisites[0]}"
47
- mv "lib/org-mode-release_#{org_version}", org_dir
48
- sh make_org_cmd(org_dir, 'compile')
49
- sh make_org_cmd(org_dir, 'autoloads')
50
- Dir.glob('lib/org-[0-9.]*').each do |ov|
51
- next if ov == org_dir
52
- rm_r ov
53
- end
46
+ Fronde::Org.compile(
47
+ task.prerequisites[0], org_version, org_dir, verbose: verbose
48
+ )
49
+ Dir.glob('lib/org-[0-9.]*').each { rm_r _1 unless _1 == org_dir }
54
50
  end
55
51
  if verbose
56
52
  build.join
57
- warn "#{org_version} has been locally installed"
53
+ warn R18n.t.fronde.tasks.org.installed(version: org_version)
58
54
  else
59
- Fronde::Utils.throbber(build, 'Installing Org:')
55
+ Fronde::CLI::Throbber.run(build, R18n.t.fronde.tasks.org.installing)
60
56
  end
61
57
  end
62
58
 
@@ -77,29 +73,53 @@ namespace :org do
77
73
  end
78
74
 
79
75
  file 'var/lib/org-config.el' => ['lib/htmlize.el', 'lib/ox-gmi.el'] do
80
- Fronde::Config.write_org_lisp_config
76
+ Fronde::CONFIG.write_org_lisp_config
81
77
  end
82
78
 
83
79
  file '.dir-locals.el' => 'var/lib/org-config.el' do
84
- Fronde::Config.write_dir_locals
80
+ Fronde::Config::Helpers.write_dir_locals
81
+ end
82
+
83
+ file '.gitignore' do
84
+ next if File.exist? '.gitignore'
85
+
86
+ upstream = File.expand_path(
87
+ '../fronde/cli/data/gitignore', __dir__
88
+ )
89
+ cp upstream, '.gitignore'
85
90
  end
86
91
 
87
92
  desc 'Install Org'
88
- multitask install: ['org:compile', '.dir-locals.el'] do
89
- mkdir_p "#{Fronde::Config.get('public_folder')}/assets"
90
- Fronde::Config.sources.each do |s|
91
- mkdir_p s['path'] unless Dir.exist? s['path']
93
+ multitask install: ['org:compile', '.gitignore'] do
94
+ # I need a fully installed org mode to correctly generate the lisp
95
+ # config
96
+ Rake::Task['.dir-locals.el'].invoke
97
+ sources = Fronde::CONFIG.sources
98
+ sources.each { mkdir_p _1['path'] }
99
+
100
+ outputs = sources.map { _1['type'] }.uniq
101
+ if outputs.include?('html')
102
+ mkdir_p "#{Fronde::CONFIG.get('html_public_folder')}/assets"
103
+ end
104
+ if outputs.include?('gemini')
105
+ mkdir_p Fronde::CONFIG.get('gemini_public_folder')
92
106
  end
93
107
  end
94
108
 
95
- # The following task only run the clobber task (not provided by us)
96
- # and the org:install one, which is already tested. Thus, we can
97
- # safely remove it from coverage.
98
- # :nocov:
99
109
  desc 'Upgrade Org'
100
110
  task :upgrade do
101
111
  Rake::Task['clobber'].execute
112
+ if File.exist? 'var/tmp/org.tar.gz'
113
+ # Cleanup cached tarball only if a new version is available.
114
+ # Also cached the new remote org version in the same time.
115
+ org_version = Fronde::Org.current_version
116
+ begin
117
+ last_version = Fronde::Org.last_version(force: true)
118
+ rescue RuntimeError
119
+ last_version = org_version
120
+ end
121
+ File.unlink 'var/tmp/org.tar.gz' unless org_version == last_version
122
+ end
102
123
  Rake::Task['org:install'].invoke
103
124
  end
104
- # :nocov:
105
125
  end