epuber 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/epuber/checker/text_checker.rb +1 -2
- data/lib/epuber/checker.rb +3 -7
- data/lib/epuber/command/build.rb +43 -7
- data/lib/epuber/command/init.rb +5 -5
- data/lib/epuber/command/server.rb +1 -1
- data/lib/epuber/command.rb +24 -19
- data/lib/epuber/compiler/compilation_context.rb +8 -4
- data/lib/epuber/compiler/file_resolver.rb +1 -1
- data/lib/epuber/compiler/file_types/source_file.rb +2 -2
- data/lib/epuber/compiler/file_types/xhtml_file.rb +7 -15
- data/lib/epuber/compiler/xhtml_processor.rb +2 -2
- data/lib/epuber/compiler.rb +13 -10
- data/lib/epuber/config.rb +35 -14
- data/lib/epuber/epubcheck.rb +82 -2
- data/lib/epuber/from_file/from_file_executor.rb +9 -9
- data/lib/epuber/plugin.rb +1 -1
- data/lib/epuber/server.rb +5 -5
- data/lib/epuber/transformer/book_transformer.rb +30 -12
- data/lib/epuber/user_interface.rb +13 -218
- data/lib/epuber/utils/location.rb +14 -0
- data/lib/epuber/utils/logger/abstract_logger.rb +214 -0
- data/lib/epuber/utils/logger/console_logger.rb +122 -0
- data/lib/epuber/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6190500e356c8d6fd0d642f6e39f2f606948933bee1b17f1285add446e5bdd6d
|
4
|
+
data.tar.gz: 63719103a8cce152f0cbb63ba72ab086064e0f5cbaf03c926fad293342be1c42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd5056baa29e1b15c5ecad2e570969a21247dec9df18d1f0e18575e4a801c0427b66b20a20a6af44a17889e5b455cc17fb55e042c203e6f154046e648630fa6b
|
7
|
+
data.tar.gz: 07a96a039ff2a06774725ca1187bc7e8c09a938513893c52b8f2d7bf6d467f7febd45529cd7beabe1ed746c7dfc4118a7936677b890681c7840edbfe4c31e8dd
|
@@ -61,8 +61,7 @@ module Epuber
|
|
61
61
|
matches = text.to_enum(:scan, regexp).map { Regexp.last_match }
|
62
62
|
matches.each do |match|
|
63
63
|
# @type match [MatchData]
|
64
|
-
UI.
|
65
|
-
Config.instance.pretty_path_from_project(file_path))
|
64
|
+
UI.warning MatchProblem.new(match, message, Config.instance.pretty_path_from_project(file_path))
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
data/lib/epuber/checker.rb
CHANGED
@@ -17,16 +17,12 @@ module Epuber
|
|
17
17
|
}.merge(super)
|
18
18
|
end
|
19
19
|
|
20
|
-
def warning(messsage, location:
|
20
|
+
def warning(messsage, location: caller_locations.first)
|
21
21
|
UI.warning(messsage, location: location)
|
22
22
|
end
|
23
23
|
|
24
|
-
def error(messsage, location:
|
25
|
-
|
26
|
-
UI.error!(messsage, location: location)
|
27
|
-
else
|
28
|
-
UI.error(messsage, location: location)
|
29
|
-
end
|
24
|
+
def error(messsage, location: caller_locations.first)
|
25
|
+
UI.error(messsage, location: location)
|
30
26
|
end
|
31
27
|
end
|
32
28
|
end
|
data/lib/epuber/command/build.rb
CHANGED
@@ -40,7 +40,7 @@ module Epuber
|
|
40
40
|
@release_version = argv.flag?('release', false)
|
41
41
|
@use_cache = argv.flag?('cache', true)
|
42
42
|
|
43
|
-
|
43
|
+
UI.logger.debug_steps_times = argv.flag?('debug-steps-times', false)
|
44
44
|
|
45
45
|
super(argv)
|
46
46
|
end
|
@@ -56,7 +56,7 @@ module Epuber
|
|
56
56
|
def run
|
57
57
|
super
|
58
58
|
|
59
|
-
UI.
|
59
|
+
UI.info "building book `#{Config.instance.pretty_path_from_project(book.file_path)}`"
|
60
60
|
|
61
61
|
if @release_version
|
62
62
|
# Remove all previous versions of compiled files
|
@@ -77,9 +77,7 @@ module Epuber
|
|
77
77
|
FileUtils.remove_file(archive_name) if ::File.exist?(archive_name)
|
78
78
|
|
79
79
|
archive_path = compiler.archive(archive_name)
|
80
|
-
|
81
|
-
Epubcheck.check(archive_path)
|
82
|
-
|
80
|
+
run_epubcheck(archive_path, build_path)
|
83
81
|
convert_epub_to_mobi(archive_path, "#{::File.basename(archive_path, '.epub')}.mobi") if target.create_mobi
|
84
82
|
|
85
83
|
Epuber::Config.instance.release_build = false
|
@@ -94,13 +92,19 @@ module Epuber
|
|
94
92
|
use_cache: @use_cache)
|
95
93
|
archive_path = compiler.archive(configuration_suffix: 'debug')
|
96
94
|
|
97
|
-
|
95
|
+
run_epubcheck(archive_path, build_path) if @should_check
|
98
96
|
|
99
97
|
convert_epub_to_mobi(archive_path, "#{::File.basename(archive_path, '.epub')}.mobi") if target.create_mobi
|
100
98
|
end
|
101
99
|
end
|
102
100
|
|
103
|
-
|
101
|
+
# Exit with error if there are any errors
|
102
|
+
if (@release_version || @should_check) && Epuber::UI.logger.error?
|
103
|
+
exit(1)
|
104
|
+
else
|
105
|
+
UI.info('🎉 Build finished successfully.'.ansi.green)
|
106
|
+
write_lockfile
|
107
|
+
end
|
104
108
|
end
|
105
109
|
|
106
110
|
private
|
@@ -131,6 +135,38 @@ module Epuber
|
|
131
135
|
path
|
132
136
|
end
|
133
137
|
|
138
|
+
def run_epubcheck(archive_path, build_dir)
|
139
|
+
UI.info("Running Epubcheck for #{archive_path}")
|
140
|
+
|
141
|
+
report = Epubcheck.check(archive_path)
|
142
|
+
report.problems.each do |problem|
|
143
|
+
relative_path = problem.location.path.sub("#{archive_path}/", '')
|
144
|
+
file_path = ::File.join(build_dir, relative_path)
|
145
|
+
|
146
|
+
nice_path = Config.instance.pretty_path_from_project(file_path)
|
147
|
+
content = ::File.read(file_path)
|
148
|
+
|
149
|
+
log_level = case problem.level
|
150
|
+
when :fatal, :error then :error
|
151
|
+
when :warning then :warning
|
152
|
+
else :info
|
153
|
+
end
|
154
|
+
message = "#{problem.level}(#{problem.code}): #{problem.message}"
|
155
|
+
|
156
|
+
p = Compiler::Problem.new(problem.level, message, content, line: problem.location.lineno,
|
157
|
+
column: problem.location.column,
|
158
|
+
length: 1,
|
159
|
+
file_path: nice_path)
|
160
|
+
UI.send(log_level, p, backtrace: nil)
|
161
|
+
end
|
162
|
+
|
163
|
+
if report.error?
|
164
|
+
UI.error('Epubcheck found some errors in epub file.')
|
165
|
+
else
|
166
|
+
UI.info('Epubcheck finished successfully.')
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
134
170
|
def find_calibre_app
|
135
171
|
locations = `mdfind "kMDItemCFBundleIdentifier == net.kovidgoyal.calibre"`.split("\n")
|
136
172
|
UI.error!("Can't find location of calibre.app to convert EPUB to MOBI.") if locations.empty?
|
data/lib/epuber/command/init.rb
CHANGED
@@ -52,7 +52,7 @@ module Epuber
|
|
52
52
|
private
|
53
53
|
|
54
54
|
def print_good_bye(book_id)
|
55
|
-
UI.
|
55
|
+
UI.info <<~TEXT.ansi.green
|
56
56
|
Project initialized, please review #{book_id}.bookspec file, remove comments and fill some attributes like book title.
|
57
57
|
TEXT
|
58
58
|
end
|
@@ -124,7 +124,7 @@ module Epuber
|
|
124
124
|
#
|
125
125
|
def write(file_path, string)
|
126
126
|
File.write(file_path, string)
|
127
|
-
UI.
|
127
|
+
UI.info " #{'create'.ansi.green} #{file_path}"
|
128
128
|
end
|
129
129
|
|
130
130
|
# @param [String] string text to file
|
@@ -148,7 +148,7 @@ module Epuber
|
|
148
148
|
existing_content << "\n"
|
149
149
|
|
150
150
|
File.write(file_path, existing_content)
|
151
|
-
UI.
|
151
|
+
UI.info " #{'update'.ansi.green} #{file_path}"
|
152
152
|
end
|
153
153
|
|
154
154
|
# @param [String] dir_path path to dir
|
@@ -157,7 +157,7 @@ module Epuber
|
|
157
157
|
#
|
158
158
|
def create_folder(dir_path)
|
159
159
|
FileUtils.mkdir_p(dir_path)
|
160
|
-
UI.
|
160
|
+
UI.info " #{'create'.ansi.green} #{dir_path}/"
|
161
161
|
end
|
162
162
|
|
163
163
|
# @param [String] text
|
@@ -169,7 +169,7 @@ module Epuber
|
|
169
169
|
result = $stdin.gets.chomp
|
170
170
|
|
171
171
|
while result.empty?
|
172
|
-
UI.
|
172
|
+
UI.info 'Value cannot be empty, please fill it!'.ansi.red
|
173
173
|
print text
|
174
174
|
result = $stdin.gets.chomp
|
175
175
|
end
|
@@ -51,7 +51,7 @@ module Epuber
|
|
51
51
|
if @open_web_browser
|
52
52
|
system "open #{uri}"
|
53
53
|
else
|
54
|
-
UI.
|
54
|
+
UI.info 'Web browser can be automatically opened by adding --open flag, see --help'
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
data/lib/epuber/command.rb
CHANGED
@@ -25,32 +25,23 @@ module Epuber
|
|
25
25
|
self.plugin_prefixes = plugin_prefixes + %w[epuber]
|
26
26
|
|
27
27
|
def self.run(argv = [])
|
28
|
-
UI.current_command = self
|
29
28
|
super
|
30
|
-
UI.current_command = nil
|
31
29
|
rescue Interrupt
|
32
30
|
UI.error('[!] Cancelled')
|
33
31
|
rescue StandardError => e
|
34
32
|
UI.error!(e)
|
35
|
-
|
36
|
-
UI.current_command = nil
|
37
33
|
end
|
38
34
|
|
39
|
-
def
|
35
|
+
def initialize(argv)
|
40
36
|
super
|
41
|
-
UI.current_command = self
|
42
|
-
end
|
43
37
|
|
44
|
-
|
45
|
-
UI.current_command = self
|
38
|
+
UI.logger.verbose = verbose?
|
46
39
|
end
|
47
40
|
|
48
|
-
|
41
|
+
def run; end
|
49
42
|
|
50
43
|
protected
|
51
44
|
|
52
|
-
attr_writer :debug_steps_times
|
53
|
-
|
54
45
|
# @return [Epuber::Book::Book]
|
55
46
|
#
|
56
47
|
def book
|
@@ -62,9 +53,16 @@ module Epuber
|
|
62
53
|
# @raise PlainInformative if no .bookspec file don't exists or there are too many
|
63
54
|
#
|
64
55
|
def verify_one_bookspec_exists!
|
65
|
-
|
66
|
-
|
67
|
-
|
56
|
+
project_path = Config.instance.project_path
|
57
|
+
bookspec_files = Config.find_bookspec_files(project_path)
|
58
|
+
|
59
|
+
if bookspec_files.empty?
|
60
|
+
raise PlainInformative, "No `.bookspec' found in the project directory (or in any parent folders)."
|
61
|
+
end
|
62
|
+
|
63
|
+
if bookspec_files.count > 1
|
64
|
+
raise PlainInformative, "Multiple `.bookspec' found in directory (directory: #{project_path})"
|
65
|
+
end
|
68
66
|
end
|
69
67
|
|
70
68
|
def write_lockfile
|
@@ -76,10 +74,17 @@ module Epuber
|
|
76
74
|
def pre_build_checks
|
77
75
|
Config.instance.warn_for_outdated_versions!
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
77
|
+
# remove build caches if we are using different version of Epuber or Bade
|
78
|
+
if !Config.instance.same_version_as_last_run? && File.exist?(Config.instance.working_path)
|
79
|
+
UI.warning('Using different version of Epuber or Bade, removing all build caches')
|
80
|
+
Config.instance.remove_build_caches
|
81
|
+
end
|
82
|
+
|
83
|
+
# ensure we are in the project directory
|
84
|
+
if Dir.pwd != Config.instance.project_path
|
85
|
+
UI.debug("Changing directory to project directory: #{Config.instance.project_path}")
|
86
|
+
Dir.chdir(Config.instance.project_path)
|
87
|
+
end
|
83
88
|
end
|
84
89
|
end
|
85
90
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
3
|
module Epuber
|
5
4
|
class Compiler
|
6
5
|
class CompilationContext
|
@@ -37,13 +36,14 @@ module Epuber
|
|
37
36
|
file_resolver.add_file(file)
|
38
37
|
end
|
39
38
|
plugin
|
40
|
-
rescue LoadError
|
41
|
-
UI.error "Can't find plugin at path #{path}"
|
39
|
+
rescue LoadError => e
|
40
|
+
UI.error "Can't find plugin at path #{path}, #{e}"
|
42
41
|
end.compact
|
43
42
|
end
|
44
43
|
|
45
44
|
# @param [Class] klass class of thing you want to perform (Checker or Transformer)
|
46
45
|
# @param [Symbol] source_type source type of that thing (Checker or Transformer)
|
46
|
+
# @param [String] processing_time_step_name name of step for processing time
|
47
47
|
#
|
48
48
|
# @yield
|
49
49
|
# @yieldparam [Epuber::CheckerTransformerBase] instance of checker or transformer
|
@@ -58,7 +58,11 @@ module Epuber
|
|
58
58
|
next if instance.source_type != source_type
|
59
59
|
next if instance.options.include?(:run_only_before_release) && !release_build
|
60
60
|
|
61
|
-
|
61
|
+
location = instance.block.source_location.map(&:to_s).join(':')
|
62
|
+
message = "performing #{source_type.inspect} from plugin #{location}"
|
63
|
+
UI.print_step_processing_time(message) do
|
64
|
+
yield instance
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
@@ -27,7 +27,7 @@ module Epuber
|
|
27
27
|
|
28
28
|
PATH_TYPES = [:spine, :manifest, :package, nil].freeze
|
29
29
|
|
30
|
-
# @return [String] path where should look for source files
|
30
|
+
# @return [String] path where should look for source files (relative to project root)
|
31
31
|
#
|
32
32
|
attr_reader :source_path
|
33
33
|
|
@@ -6,7 +6,7 @@ module Epuber
|
|
6
6
|
require_relative 'abstract_file'
|
7
7
|
|
8
8
|
class SourceFile < AbstractFile
|
9
|
-
# @return [String] relative source path
|
9
|
+
# @return [String] relative source path (from project root)
|
10
10
|
#
|
11
11
|
attr_reader :source_path
|
12
12
|
|
@@ -133,7 +133,7 @@ module Epuber
|
|
133
133
|
rescue XHTMLProcessor::UnparseableLinkError,
|
134
134
|
FileFinders::FileNotFoundError,
|
135
135
|
FileFinders::MultipleFilesFoundError => e
|
136
|
-
UI.
|
136
|
+
UI.error(e.to_s, location: location)
|
137
137
|
return nil
|
138
138
|
end
|
139
139
|
end
|
@@ -142,18 +142,14 @@ module Epuber
|
|
142
142
|
end
|
143
143
|
|
144
144
|
# perform transformations
|
145
|
-
|
146
|
-
|
147
|
-
xhtml_string = transformer.call(final_destination_path, xhtml_string, compilation_context)
|
148
|
-
end
|
145
|
+
compilation_context.perform_plugin_things(Transformer, :result_text_xhtml_string) do |transformer|
|
146
|
+
xhtml_string = transformer.call(final_destination_path, xhtml_string, compilation_context)
|
149
147
|
end
|
150
148
|
|
151
149
|
# perform custom validation
|
152
150
|
if compilation_context.should_check
|
153
|
-
|
154
|
-
|
155
|
-
checker.call(final_destination_path, xhtml_string, compilation_context)
|
156
|
-
end
|
151
|
+
compilation_context.perform_plugin_things(Checker, :result_text_xhtml_string) do |checker|
|
152
|
+
checker.call(final_destination_path, xhtml_string, compilation_context)
|
157
153
|
end
|
158
154
|
end
|
159
155
|
|
@@ -175,7 +171,7 @@ module Epuber
|
|
175
171
|
# @param [Compiler::CompilationContext] compilation_context
|
176
172
|
# @param [Hash<String, XHTMLFile>] global_ids
|
177
173
|
#
|
178
|
-
def process_global_ids(
|
174
|
+
def process_global_ids(_compilation_context, global_ids)
|
179
175
|
return if self.global_ids.empty? && global_links.empty?
|
180
176
|
|
181
177
|
xhtml_doc = XHTMLProcessor.xml_document_from_string(File.read(final_destination_path), final_destination_path)
|
@@ -196,12 +192,8 @@ module Epuber
|
|
196
192
|
node['href'] = "#{rel_path}##{href}"
|
197
193
|
else
|
198
194
|
message = "Can't find global id '#{href}' from link in file #{source_path}"
|
199
|
-
location =
|
200
|
-
|
201
|
-
UI.error!(message, location: location)
|
202
|
-
else
|
203
|
-
UI.warning(message, location: location)
|
204
|
-
end
|
195
|
+
location = Epuber::Location.new(path: final_destination_path, lineno: node.line)
|
196
|
+
UI.error(message, location: location)
|
205
197
|
end
|
206
198
|
end
|
207
199
|
|
@@ -25,7 +25,7 @@ module Epuber
|
|
25
25
|
|
26
26
|
if /\A[\n\r ]+(<\?xml)/ =~ text
|
27
27
|
UI.warning('XML header must be at the beginning of document',
|
28
|
-
location:
|
28
|
+
location: Epuber::Location.new(path: file_path, lineno: 1))
|
29
29
|
|
30
30
|
text = text.lstrip
|
31
31
|
end
|
@@ -198,7 +198,7 @@ module Epuber
|
|
198
198
|
# @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching
|
199
199
|
# for file from tag <img>
|
200
200
|
# @param [String] file_path path to file from which is searching for other file
|
201
|
-
# @param [Epuber::Compiler::
|
201
|
+
# @param [Epuber::Compiler::FileFinders::Abstract] file_finder finder for searching for files
|
202
202
|
#
|
203
203
|
# @raise UnparseableLinkError, FileFinder::FileNotFoundError, FileFinder::MultipleFilesFoundError
|
204
204
|
#
|
data/lib/epuber/compiler.rb
CHANGED
@@ -71,7 +71,7 @@ module Epuber
|
|
71
71
|
|
72
72
|
FileUtils.mkdir_p(build_folder)
|
73
73
|
|
74
|
-
UI.
|
74
|
+
UI.info " #{<<~MSG}"
|
75
75
|
building target #{@target.name.inspect} (build dir: #{Config.instance.pretty_path_from_project(build_folder)})
|
76
76
|
MSG
|
77
77
|
|
@@ -89,6 +89,7 @@ module Epuber
|
|
89
89
|
process_all_target_files
|
90
90
|
generate_other_files
|
91
91
|
|
92
|
+
# run :after_all_text_files transformers
|
92
93
|
compilation_context.perform_plugin_things(Transformer, :after_all_text_files) do |transformer|
|
93
94
|
transformer.call(@book, compilation_context)
|
94
95
|
end
|
@@ -131,7 +132,7 @@ module Epuber
|
|
131
132
|
old_paths = zip_file.instance_eval { @entry_set.entries.map(&:name) }
|
132
133
|
diff = old_paths - new_paths
|
133
134
|
diff.each do |file_to_remove|
|
134
|
-
UI.
|
135
|
+
UI.debug "removing file from result EPUB: #{file_to_remove}"
|
135
136
|
zip_file.remove(file_to_remove)
|
136
137
|
end
|
137
138
|
end
|
@@ -174,7 +175,7 @@ module Epuber
|
|
174
175
|
.select { |d| File.directory?(d) }
|
175
176
|
.select { |d| (Dir.entries(d) - %w[. ..]).empty? }
|
176
177
|
.each do |d|
|
177
|
-
UI.
|
178
|
+
UI.debug "removing empty folder `#{d}`"
|
178
179
|
Dir.rmdir(d)
|
179
180
|
end
|
180
181
|
end
|
@@ -188,7 +189,7 @@ module Epuber
|
|
188
189
|
end
|
189
190
|
unnecessary_paths.each do |path|
|
190
191
|
if compilation_context.verbose?
|
191
|
-
UI.
|
192
|
+
UI.debug "removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`"
|
192
193
|
end
|
193
194
|
|
194
195
|
File.delete(path)
|
@@ -283,11 +284,11 @@ module Epuber
|
|
283
284
|
#
|
284
285
|
def process_all_target_files
|
285
286
|
@file_resolver.manifest_files.each_with_index do |file, idx|
|
286
|
-
UI.
|
287
|
+
UI.start_processing_file(file, idx, @file_resolver.manifest_files.count)
|
287
288
|
process_file(file)
|
288
289
|
end
|
289
290
|
|
290
|
-
UI.
|
291
|
+
UI.end_processing
|
291
292
|
end
|
292
293
|
|
293
294
|
# @param [Epuber::Book::TocItem] toc_item
|
@@ -305,11 +306,13 @@ module Epuber
|
|
305
306
|
end
|
306
307
|
|
307
308
|
def process_global_ids
|
308
|
-
|
309
|
-
|
309
|
+
UI.print_step_processing_time('Processing global ids') do
|
310
|
+
xhtml_files = @file_resolver.files.select { |file| file.is_a?(FileTypes::XHTMLFile) }
|
311
|
+
global_ids = validate_global_ids(xhtml_files)
|
310
312
|
|
311
|
-
|
312
|
-
|
313
|
+
xhtml_files.each do |file|
|
314
|
+
file.process_global_ids(compilation_context, global_ids)
|
315
|
+
end
|
313
316
|
end
|
314
317
|
end
|
315
318
|
|
data/lib/epuber/config.rb
CHANGED
@@ -8,10 +8,13 @@ module Epuber
|
|
8
8
|
class Config
|
9
9
|
WORKING_PATH = '.epuber'
|
10
10
|
|
11
|
-
# @return [String]
|
11
|
+
# @return [String] path to project directory (where .bookspec file is located or current directory if not found)
|
12
12
|
#
|
13
13
|
def project_path
|
14
|
-
@project_path ||=
|
14
|
+
@project_path ||= begin
|
15
|
+
path = self.class.find_project_dir(Dir.pwd) || Dir.pwd
|
16
|
+
path.unicode_normalize
|
17
|
+
end
|
15
18
|
end
|
16
19
|
|
17
20
|
# @param [String] of_file absolute path to file
|
@@ -19,7 +22,9 @@ module Epuber
|
|
19
22
|
# @return [String] relative path to file from root of project
|
20
23
|
#
|
21
24
|
def pretty_path_from_project(of_file)
|
22
|
-
Pathname.new(of_file.unicode_normalize)
|
25
|
+
Pathname.new(of_file.unicode_normalize)
|
26
|
+
.relative_path_from(Pathname.new(project_path))
|
27
|
+
.to_s
|
23
28
|
end
|
24
29
|
|
25
30
|
# @return [String]
|
@@ -31,7 +36,7 @@ module Epuber
|
|
31
36
|
# @return [String]
|
32
37
|
#
|
33
38
|
def bookspec_path
|
34
|
-
@bookspec_path ||=
|
39
|
+
@bookspec_path ||= self.class.find_bookspec_files(project_path).first
|
35
40
|
end
|
36
41
|
|
37
42
|
# @return [String]
|
@@ -40,16 +45,6 @@ module Epuber
|
|
40
45
|
"#{bookspec_path}.lock"
|
41
46
|
end
|
42
47
|
|
43
|
-
# @return [Array<String>]
|
44
|
-
#
|
45
|
-
def find_all_bookspecs
|
46
|
-
Dir.chdir(project_path) do
|
47
|
-
Dir.glob('*.bookspec').map do |path|
|
48
|
-
File.expand_path(path)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
48
|
# @return [Epuber::Book]
|
54
49
|
#
|
55
50
|
def bookspec
|
@@ -182,6 +177,32 @@ module Epuber
|
|
182
177
|
|
183
178
|
book
|
184
179
|
end
|
180
|
+
|
181
|
+
# Find all bookspec files in given directory
|
182
|
+
#
|
183
|
+
# @param [String] dir
|
184
|
+
#
|
185
|
+
def find_bookspec_files(dir)
|
186
|
+
Dir.chdir(dir) do
|
187
|
+
Dir.glob('*.bookspec').map do |path|
|
188
|
+
File.expand_path(path)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Find project directory by searching for .bookspec files in current and parent directories
|
194
|
+
#
|
195
|
+
# @param [String] dir
|
196
|
+
# @return [String, nil]
|
197
|
+
#
|
198
|
+
def find_project_dir(dir)
|
199
|
+
return dir if find_bookspec_files(dir).any?
|
200
|
+
|
201
|
+
parent = File.dirname(dir)
|
202
|
+
return nil if parent == dir
|
203
|
+
|
204
|
+
find_project_dir(parent)
|
205
|
+
end
|
185
206
|
end
|
186
207
|
|
187
208
|
self.test = false
|
data/lib/epuber/epubcheck.rb
CHANGED
@@ -1,14 +1,94 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'open3'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require_relative 'utils/location'
|
7
|
+
|
3
8
|
module Epuber
|
4
9
|
class Epubcheck
|
10
|
+
Report = Struct.new(:problems, keyword_init: true) do
|
11
|
+
# !attribute [r] problems
|
12
|
+
# @return [Array<Problem>] problems found by epubcheck
|
13
|
+
|
14
|
+
def error?
|
15
|
+
problems.any?(&:error?)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Problem = Struct.new(:level, :code, :location, :message, keyword_init: true) do
|
20
|
+
# !attribute [r] level
|
21
|
+
# @return [Symbol] level of the problem (:fatal, :error, :warning, :info, :usage, :suppressed)
|
22
|
+
|
23
|
+
# !attribute [r] code
|
24
|
+
# @return [String] code of the problem (for example: RSC-005)
|
25
|
+
|
26
|
+
# !attribute [r] location
|
27
|
+
# @return [Epuber::Location, nil] location of the problem
|
28
|
+
|
29
|
+
# !attribute [r] message
|
30
|
+
# @return [String] message of the problem
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
"#{level}(#{code}): #{location.path}(#{location.lineno},#{location.column}): #{message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def error?
|
37
|
+
level == :error || level == :fatal
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.from_json(json)
|
41
|
+
json_location = json['locations'].first
|
42
|
+
|
43
|
+
location = if json_location
|
44
|
+
Epuber::Location.new(
|
45
|
+
path: json_location['path'],
|
46
|
+
lineno: json_location['line'],
|
47
|
+
column: json_location['column'],
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
new(
|
52
|
+
level: json['severity'].downcase.to_sym,
|
53
|
+
code: json['ID'],
|
54
|
+
message: json['message'],
|
55
|
+
location: location,
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
5
60
|
class << self
|
6
61
|
# @param [String] path path to file
|
7
62
|
#
|
63
|
+
# @return [Report] report of the epubcheck
|
64
|
+
#
|
8
65
|
def check(path)
|
9
|
-
|
66
|
+
report = nil
|
67
|
+
|
68
|
+
Dir.mktmpdir('epubcheck-') do |tmpdir|
|
69
|
+
json_path = File.join(tmpdir, 'epubcheck.json')
|
70
|
+
Open3.popen3('epubcheck', path, '--json', json_path) do |_stdin, _stdout, stderr, wait_thr|
|
71
|
+
exit_status = wait_thr.value
|
72
|
+
|
73
|
+
if exit_status.success?
|
74
|
+
report = _parse_json(File.read(json_path))
|
75
|
+
else
|
76
|
+
UI.error(stderr.gets.chomp)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
report
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param [String] string json string
|
85
|
+
# @return [Report]
|
86
|
+
#
|
87
|
+
def _parse_json(string)
|
88
|
+
json = JSON.parse(string)
|
89
|
+
messages = json['messages']
|
10
90
|
|
11
|
-
|
91
|
+
Report.new(problems: messages.map { |msg| Problem.from_json(msg) })
|
12
92
|
end
|
13
93
|
end
|
14
94
|
end
|