epuber 0.7.4 → 0.9.0
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.
- checksums.yaml +4 -4
- data/Gemfile +10 -2
- data/LICENSE.txt +1 -1
- data/README.md +4 -3
- data/epuber.gemspec +11 -16
- data/lib/epuber/book/contributor.rb +10 -7
- data/lib/epuber/book/file_request.rb +3 -3
- data/lib/epuber/book/target.rb +30 -33
- data/lib/epuber/book/toc_item.rb +2 -4
- data/lib/epuber/book.rb +21 -21
- data/lib/epuber/checker/bookspec_checker.rb +26 -0
- data/lib/epuber/checker/text_checker.rb +16 -7
- data/lib/epuber/checker.rb +16 -2
- data/lib/epuber/checker_transformer_base.rb +3 -6
- data/lib/epuber/command/build.rb +40 -25
- data/lib/epuber/command/from_file.rb +39 -0
- data/lib/epuber/command/init.rb +34 -32
- data/lib/epuber/command/server.rb +3 -3
- data/lib/epuber/command.rb +18 -20
- data/lib/epuber/compiler/compilation_context.rb +10 -8
- data/lib/epuber/compiler/file_database.rb +2 -4
- data/lib/epuber/compiler/file_finders/abstract.rb +36 -26
- data/lib/epuber/compiler/file_finders/imaginary.rb +40 -35
- data/lib/epuber/compiler/file_resolver.rb +79 -89
- data/lib/epuber/compiler/file_stat.rb +4 -4
- data/lib/epuber/compiler/file_types/abstract_file.rb +4 -7
- data/lib/epuber/compiler/file_types/bade_file.rb +20 -15
- data/lib/epuber/compiler/file_types/coffee_script_file.rb +1 -1
- data/lib/epuber/compiler/file_types/css_file.rb +103 -0
- data/lib/epuber/compiler/file_types/generated_file.rb +1 -1
- data/lib/epuber/compiler/file_types/image_file.rb +4 -2
- data/lib/epuber/compiler/file_types/nav_file.rb +0 -1
- data/lib/epuber/compiler/file_types/opf_file.rb +0 -1
- data/lib/epuber/compiler/file_types/source_file.rb +8 -3
- data/lib/epuber/compiler/file_types/stylus_file.rb +4 -3
- data/lib/epuber/compiler/file_types/xhtml_file.rb +67 -13
- data/lib/epuber/compiler/generator.rb +1 -2
- data/lib/epuber/compiler/meta_inf_generator.rb +1 -1
- data/lib/epuber/compiler/nav_generator.rb +10 -11
- data/lib/epuber/compiler/opf_generator.rb +26 -27
- data/lib/epuber/compiler/problem.rb +12 -21
- data/lib/epuber/compiler/xhtml_processor.rb +63 -32
- data/lib/epuber/compiler.rb +77 -25
- data/lib/epuber/config.rb +16 -10
- data/lib/epuber/dsl/attribute.rb +17 -18
- data/lib/epuber/dsl/attribute_support.rb +7 -7
- data/lib/epuber/dsl/object.rb +19 -17
- data/lib/epuber/dsl/tree_object.rb +2 -3
- data/lib/epuber/epubcheck.rb +15 -0
- data/lib/epuber/from_file/bookspec_generator.rb +371 -0
- data/lib/epuber/from_file/encryption_handler.rb +146 -0
- data/lib/epuber/from_file/from_file_executor.rb +140 -0
- data/lib/epuber/from_file/nav_file.rb +163 -0
- data/lib/epuber/from_file/opf_file.rb +219 -0
- data/lib/epuber/helper.rb +0 -1
- data/lib/epuber/lockfile.rb +7 -9
- data/lib/epuber/plugin.rb +2 -3
- data/lib/epuber/ruby_extensions/match_data.rb +1 -1
- data/lib/epuber/ruby_extensions/thread.rb +1 -0
- data/lib/epuber/server/base.styl +0 -1
- data/lib/epuber/server/basic.styl +1 -30
- data/lib/epuber/server/handlers.rb +1 -1
- data/lib/epuber/server.rb +81 -80
- data/lib/epuber/third_party/bower.rb +5 -5
- data/lib/epuber/transformer/book_transformer.rb +108 -0
- data/lib/epuber/transformer/text_transformer.rb +4 -2
- data/lib/epuber/transformer.rb +4 -2
- data/lib/epuber/user_interface.rb +49 -38
- data/lib/epuber/vendor/hash_binding.rb +9 -2
- data/lib/epuber/vendor/ruby_templater.rb +4 -8
- data/lib/epuber/vendor/version.rb +12 -12
- data/lib/epuber/version.rb +1 -1
- metadata +79 -100
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
@@ -11,6 +11,8 @@ module Epuber
|
|
11
11
|
#
|
12
12
|
attr_accessor :entries
|
13
13
|
|
14
|
+
# @return [String]
|
15
|
+
#
|
14
16
|
attr_reader :name
|
15
17
|
|
16
18
|
def initialize(name)
|
@@ -32,8 +34,12 @@ module Epuber
|
|
32
34
|
end
|
33
35
|
|
34
36
|
class FileEntry
|
37
|
+
# @return [String]
|
38
|
+
#
|
35
39
|
attr_reader :name
|
36
40
|
|
41
|
+
# @return [String]
|
42
|
+
#
|
37
43
|
attr_reader :absolute_path
|
38
44
|
|
39
45
|
def initialize(name, absolute_path)
|
@@ -42,7 +48,7 @@ module Epuber
|
|
42
48
|
end
|
43
49
|
|
44
50
|
def ==(other)
|
45
|
-
other.is_a?(FileEntry) ?
|
51
|
+
name == (other.is_a?(FileEntry) ? other.name : other.to_s)
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
@@ -83,14 +89,11 @@ module Epuber
|
|
83
89
|
make_dir_p(path)[file_name] = FileEntry.new(file_name, file_path)
|
84
90
|
end
|
85
91
|
|
86
|
-
|
87
92
|
def __core_find_files_from_pattern(pattern)
|
88
93
|
parts = self.class.path_parts(pattern)
|
89
94
|
found_entries = find_recurser(root, parts).flatten
|
90
95
|
file_entries = found_entries.reject { |entry| entry.is_a?(DirEntry) }
|
91
|
-
file_entries.map
|
92
|
-
item.absolute_path
|
93
|
-
end
|
96
|
+
file_entries.map(&:absolute_path)
|
94
97
|
end
|
95
98
|
|
96
99
|
def __core_file?(path)
|
@@ -104,12 +107,14 @@ module Epuber
|
|
104
107
|
current.is_a?(FileEntry)
|
105
108
|
end
|
106
109
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
110
|
+
class << self
|
111
|
+
def path_parts(path)
|
112
|
+
path.split(File::SEPARATOR).reject(&:empty?)
|
113
|
+
end
|
111
114
|
end
|
112
115
|
|
116
|
+
private
|
117
|
+
|
113
118
|
def find_recurser(dir, parts)
|
114
119
|
return [] unless dir.respond_to? :[]
|
115
120
|
|
@@ -117,25 +122,25 @@ module Epuber
|
|
117
122
|
return [] if pattern.nil?
|
118
123
|
|
119
124
|
matches = case pattern
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
else
|
134
|
-
directories_under(dir)
|
135
|
-
end
|
125
|
+
when '**'
|
126
|
+
case parts
|
127
|
+
when ['*']
|
128
|
+
parts = [] # end recursion
|
129
|
+
directories_under(dir).map do |d|
|
130
|
+
d.entries.select do |f|
|
131
|
+
(f.is_a?(FileEntry) || f.is_a?(DirEntry)) &&
|
132
|
+
f.name.match(/\A(?!\.)/)
|
133
|
+
end
|
134
|
+
end.flatten.uniq
|
135
|
+
when []
|
136
|
+
parts = [] # end recursion
|
137
|
+
dir.entries.values.flatten.uniq
|
136
138
|
else
|
137
|
-
|
138
|
-
|
139
|
+
directories_under(dir)
|
140
|
+
end
|
141
|
+
else
|
142
|
+
regex_body = pattern_to_regex(pattern)
|
143
|
+
dir.entries.select { |k, _v| /\A#{regex_body}\Z/ =~ k }.values
|
139
144
|
end
|
140
145
|
|
141
146
|
if parts.empty? # we're done recursing
|
@@ -156,14 +161,14 @@ module Epuber
|
|
156
161
|
|
157
162
|
def pattern_to_regex(pattern)
|
158
163
|
pattern.gsub('.', '\.')
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
164
|
+
.gsub('?', '.')
|
165
|
+
.gsub('*', '.*')
|
166
|
+
.gsub('(', '\(')
|
167
|
+
.gsub(')', '\)')
|
168
|
+
.gsub(/\{(.*?)\}/) do
|
169
|
+
"(#{Regexp.last_match[1].gsub(',', '|')})"
|
170
|
+
end
|
171
|
+
.gsub(/\A\./, '(?!\.).')
|
167
172
|
end
|
168
173
|
end
|
169
174
|
end
|
@@ -20,12 +20,12 @@ module Epuber
|
|
20
20
|
require_relative 'file_types/container_xml_file'
|
21
21
|
require_relative 'file_types/ibooks_display_options_file'
|
22
22
|
require_relative 'file_types/coffee_script_file'
|
23
|
-
|
23
|
+
require_relative 'file_types/css_file'
|
24
24
|
|
25
25
|
class FileResolver
|
26
26
|
class ResolveError < StandardError; end
|
27
27
|
|
28
|
-
PATH_TYPES = [:spine, :manifest, :package, nil]
|
28
|
+
PATH_TYPES = [:spine, :manifest, :package, nil].freeze
|
29
29
|
|
30
30
|
# @return [String] path where should look for source files
|
31
31
|
#
|
@@ -125,24 +125,16 @@ module Epuber
|
|
125
125
|
existing_file = @final_destination_path_to_file[file.final_destination_path]
|
126
126
|
|
127
127
|
# save mapping from file_request to file, file_request can be different, but result file could be the same ...
|
128
|
-
unless file.try(:file_request).nil?
|
129
|
-
@request_to_files[file.file_request] << (existing_file || file)
|
130
|
-
end
|
128
|
+
@request_to_files[file.file_request] << (existing_file || file) unless file.try(:file_request).nil?
|
131
129
|
|
132
130
|
# return existing file if already exists, new file will be thrown away
|
133
131
|
return existing_file unless existing_file.nil?
|
134
132
|
|
135
|
-
if [:spine].include?(type)
|
136
|
-
@spine_files << file
|
137
|
-
end
|
133
|
+
@spine_files << file if [:spine].include?(type)
|
138
134
|
|
139
|
-
if [
|
140
|
-
@manifest_files << file
|
141
|
-
end
|
135
|
+
@manifest_files << file if %i[spine manifest].include?(type)
|
142
136
|
|
143
|
-
if [
|
144
|
-
@package_files << file
|
145
|
-
end
|
137
|
+
@package_files << file if %i[spine manifest package].include?(type)
|
146
138
|
|
147
139
|
@files << file
|
148
140
|
|
@@ -151,13 +143,11 @@ module Epuber
|
|
151
143
|
|
152
144
|
@final_destination_path_to_file[file.final_destination_path] = file
|
153
145
|
|
154
|
-
if file.respond_to?(:source_path) && !file.source_path.nil?
|
155
|
-
@source_path_to_file[file.source_path] = file
|
156
|
-
end
|
146
|
+
@source_path_to_file[file.source_path] = file if file.respond_to?(:source_path) && !file.source_path.nil?
|
157
147
|
|
158
|
-
|
159
|
-
|
160
|
-
|
148
|
+
return unless file.respond_to?(:abs_source_path) && !file.abs_source_path.nil?
|
149
|
+
|
150
|
+
@abs_source_path_to_file[file.abs_source_path] = file
|
161
151
|
end
|
162
152
|
|
163
153
|
# Get instance of file from request instance
|
@@ -184,7 +174,7 @@ module Epuber
|
|
184
174
|
end
|
185
175
|
|
186
176
|
if file_request.only_one
|
187
|
-
files.first
|
177
|
+
files.first # @request_to_files always returns array, see #initialize method
|
188
178
|
else
|
189
179
|
files
|
190
180
|
end
|
@@ -219,99 +209,99 @@ module Epuber
|
|
219
209
|
# @return [Array<String>] list of files that should be deleted in destination directory
|
220
210
|
#
|
221
211
|
def unneeded_files_in_destination
|
222
|
-
requested_paths = files.map
|
223
|
-
file.pkg_destination_path
|
224
|
-
end
|
212
|
+
requested_paths = files.map(&:pkg_destination_path)
|
225
213
|
|
226
214
|
existing_paths = FileFinders::Normal.new(destination_path).find_all('*')
|
227
215
|
|
228
216
|
unnecessary_paths = existing_paths - requested_paths
|
229
217
|
|
230
|
-
unnecessary_paths.
|
231
|
-
|
218
|
+
unnecessary_paths.reject! do |path|
|
219
|
+
::File.directory?(File.join(destination_path, path))
|
232
220
|
end
|
233
221
|
|
234
222
|
unnecessary_paths
|
235
223
|
end
|
236
224
|
|
237
|
-
|
238
225
|
##################################################################################################################
|
239
226
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
# @param file [Epuber::Compiler::AbstractFile]
|
258
|
-
#
|
259
|
-
# @return [nil]
|
260
|
-
#
|
261
|
-
def resolve_destination_path(file)
|
262
|
-
if file.final_destination_path.nil?
|
263
|
-
dest_path = if file.respond_to?(:source_path) && !file.source_path.nil?
|
264
|
-
file.abs_source_path = File.expand_path(file.source_path, source_path)
|
265
|
-
self.class.renamed_file_with_path(file.source_path)
|
266
|
-
elsif !file.destination_path.nil?
|
267
|
-
file.destination_path
|
268
|
-
else
|
269
|
-
raise ResolveError, "What should I do with file that doesn't have source path or destination path? file: #{file.inspect}"
|
270
|
-
end
|
271
|
-
|
272
|
-
file.destination_path = dest_path
|
273
|
-
file.pkg_destination_path = File.join(*self.class.path_comps_for(file.path_type), dest_path)
|
274
|
-
file.final_destination_path = File.join(destination_path, file.pkg_destination_path)
|
227
|
+
class << self
|
228
|
+
# @param [String] path path to some file
|
229
|
+
#
|
230
|
+
# @return [String] path with changed extension
|
231
|
+
#
|
232
|
+
def renamed_file_with_path(path)
|
233
|
+
extname = File.extname(path)
|
234
|
+
new_extname = FileFinders::EXTENSIONS_RENAME[extname]
|
235
|
+
|
236
|
+
if new_extname.nil?
|
237
|
+
path
|
238
|
+
else
|
239
|
+
path.sub(/#{Regexp.escape(extname)}$/, new_extname)
|
240
|
+
end
|
275
241
|
end
|
276
|
-
end
|
277
242
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
243
|
+
# @param [String] extname extension of file
|
244
|
+
#
|
245
|
+
# @return [Class]
|
246
|
+
#
|
247
|
+
def file_class_for(extname)
|
248
|
+
mapping = {
|
249
|
+
'.styl' => FileTypes::StylusFile,
|
250
|
+
'.css' => FileTypes::CSSFile,
|
285
251
|
|
286
|
-
|
252
|
+
'.coffee' => FileTypes::CoffeeScriptFile,
|
287
253
|
|
288
|
-
|
289
|
-
|
290
|
-
|
254
|
+
'.bade' => FileTypes::BadeFile,
|
255
|
+
'.xhtml' => FileTypes::XHTMLFile,
|
256
|
+
'.html' => FileTypes::XHTMLFile,
|
291
257
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
258
|
+
'.jpg' => FileTypes::ImageFile,
|
259
|
+
'.jpeg' => FileTypes::ImageFile,
|
260
|
+
'.png' => FileTypes::ImageFile,
|
261
|
+
}
|
296
262
|
|
297
|
-
|
298
|
-
|
263
|
+
mapping[extname] || FileTypes::StaticFile
|
264
|
+
end
|
299
265
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
266
|
+
# @param [String] root_path path to root of the package
|
267
|
+
# @param [Symbol] path_type path type of file
|
268
|
+
#
|
269
|
+
# @return [Array<String>] path components
|
270
|
+
#
|
271
|
+
def path_comps_for(root_path, path_type)
|
272
|
+
case path_type
|
307
273
|
when :spine, :manifest
|
308
274
|
Array(root_path) + [Compiler::EPUB_CONTENT_FOLDER]
|
309
275
|
when :package
|
310
276
|
Array(root_path)
|
311
|
-
|
312
|
-
nil
|
277
|
+
end
|
313
278
|
end
|
314
279
|
end
|
280
|
+
|
281
|
+
private
|
282
|
+
|
283
|
+
# @param [Epuber::Compiler::AbstractFile] file
|
284
|
+
#
|
285
|
+
# @return [nil]
|
286
|
+
#
|
287
|
+
def resolve_destination_path(file)
|
288
|
+
return unless file.final_destination_path.nil?
|
289
|
+
|
290
|
+
dest_path = if file.respond_to?(:source_path) && !file.source_path.nil?
|
291
|
+
file.abs_source_path = File.expand_path(file.source_path, source_path)
|
292
|
+
self.class.renamed_file_with_path(file.source_path)
|
293
|
+
elsif !file.destination_path.nil?
|
294
|
+
file.destination_path
|
295
|
+
else
|
296
|
+
raise ResolveError, <<~ERROR
|
297
|
+
What should I do with file that doesn't have source path or destination path? file: #{file.inspect}
|
298
|
+
ERROR
|
299
|
+
end
|
300
|
+
|
301
|
+
file.destination_path = dest_path
|
302
|
+
file.pkg_destination_path = File.join(*self.class.path_comps_for(nil, file.path_type), dest_path)
|
303
|
+
file.final_destination_path = File.join(destination_path, file.pkg_destination_path)
|
304
|
+
end
|
315
305
|
end
|
316
306
|
end
|
317
307
|
end
|
@@ -36,7 +36,7 @@ module Epuber
|
|
36
36
|
@mtime = stat.mtime
|
37
37
|
@ctime = stat.ctime
|
38
38
|
@size = stat.size
|
39
|
-
rescue
|
39
|
+
rescue StandardError
|
40
40
|
# noop
|
41
41
|
end
|
42
42
|
end
|
@@ -66,9 +66,9 @@ module Epuber
|
|
66
66
|
raise AttributeError, "other must be class of #{self.class}" unless other.is_a?(FileStat)
|
67
67
|
|
68
68
|
file_path == other.file_path &&
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
size == other.size &&
|
70
|
+
mtime == other.mtime &&
|
71
|
+
ctime == other.ctime
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -4,18 +4,18 @@ module Epuber
|
|
4
4
|
class Compiler
|
5
5
|
module FileTypes
|
6
6
|
class AbstractFile
|
7
|
-
|
8
7
|
# @return [String] relative destination path
|
9
8
|
#
|
10
9
|
attr_accessor :destination_path
|
11
10
|
|
12
|
-
# @return [Symbol] group of this file (:text, :image, :font, ...), see
|
11
|
+
# @return [Symbol] group of this file (:text, :image, :font, ...), see
|
12
|
+
# Epuber::Compiler::FileFinder::GROUP_EXTENSIONS
|
13
13
|
#
|
14
14
|
attr_accessor :group
|
15
15
|
|
16
16
|
# @return [Set<Symbol>] list of properties
|
17
17
|
#
|
18
|
-
|
18
|
+
attr_writer :properties
|
19
19
|
|
20
20
|
# @return [Set<Symbol>] list of properties
|
21
21
|
#
|
@@ -23,7 +23,6 @@ module Epuber
|
|
23
23
|
@properties ||= Set.new
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
26
|
# @return [String] final relative destination path from root of the package calculated by FileResolver
|
28
27
|
#
|
29
28
|
attr_accessor :pkg_destination_path
|
@@ -88,9 +87,7 @@ module Epuber
|
|
88
87
|
def self.write_to_file!(content, to_path)
|
89
88
|
FileUtils.mkdir_p(File.dirname(to_path))
|
90
89
|
|
91
|
-
File.
|
92
|
-
file_handle.write(content)
|
93
|
-
end
|
90
|
+
File.write(to_path, content)
|
94
91
|
end
|
95
92
|
end
|
96
93
|
end
|
@@ -26,10 +26,15 @@ module Epuber
|
|
26
26
|
__file_resolver: file_resolver,
|
27
27
|
__file: self,
|
28
28
|
__toc_item: toc_item,
|
29
|
-
__const: Hash.new
|
29
|
+
__const: Hash.new do |_hash, key|
|
30
|
+
UI.warning("Undefined constant with key `#{key}`", location: caller_locations[0])
|
31
|
+
end.merge!(target.constants),
|
30
32
|
}
|
31
33
|
|
32
|
-
should_load_from_precompiled = up_to_date &&
|
34
|
+
should_load_from_precompiled = up_to_date &&
|
35
|
+
precompiled_exists &&
|
36
|
+
compilation_context.incremental_build? &&
|
37
|
+
!compilation_context.should_write
|
33
38
|
|
34
39
|
precompiled = if should_load_from_precompiled
|
35
40
|
begin
|
@@ -40,16 +45,10 @@ module Epuber
|
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
43
|
-
if
|
44
|
-
|
45
|
-
|
46
|
-
.with_locals(variables)
|
47
|
-
renderer.file_path = source_path
|
48
|
-
|
49
|
-
renderer.render(new_line: '', indent: '')
|
48
|
+
if precompiled.nil?
|
49
|
+
if compilation_context.incremental_build?
|
50
|
+
UI.print_processing_debug_info('Parsing new version of source file')
|
50
51
|
end
|
51
|
-
else
|
52
|
-
UI.print_processing_debug_info('Parsing new version of source file') if compilation_context.incremental_build?
|
53
52
|
|
54
53
|
bade_content = load_source(compilation_context)
|
55
54
|
|
@@ -58,13 +57,19 @@ module Epuber
|
|
58
57
|
.with_locals(variables)
|
59
58
|
|
60
59
|
# turn on optimizations when can
|
61
|
-
if renderer.respond_to?(:optimize=)
|
62
|
-
renderer.optimize = true
|
63
|
-
end
|
60
|
+
renderer.optimize = true if renderer.respond_to?(:optimize=)
|
64
61
|
|
65
62
|
FileUtils.mkdir_p(File.dirname(precompiled_path))
|
66
63
|
renderer.precompiled.write_yaml_to_file(precompiled_path)
|
67
64
|
|
65
|
+
renderer.render(new_line: '', indent: '')
|
66
|
+
end
|
67
|
+
else
|
68
|
+
xhtml_content = UI.print_step_processing_time('rendering precompiled Bade') do
|
69
|
+
renderer = Bade::Renderer.from_precompiled(precompiled)
|
70
|
+
.with_locals(variables)
|
71
|
+
renderer.file_path = source_path
|
72
|
+
|
68
73
|
renderer.render(new_line: '', indent: '')
|
69
74
|
end
|
70
75
|
end
|
@@ -82,7 +87,7 @@ module Epuber
|
|
82
87
|
# @return [String]
|
83
88
|
#
|
84
89
|
def precompiled_path
|
85
|
-
File.join(Config.instance.build_cache_path(PRECOMPILED_CACHE_NAME), source_path
|
90
|
+
File.join(Config.instance.build_cache_path(PRECOMPILED_CACHE_NAME), "#{source_path}.precompiled.yml")
|
86
91
|
end
|
87
92
|
|
88
93
|
def pretty_precompiled_path
|
@@ -10,7 +10,7 @@ module Epuber
|
|
10
10
|
class CoffeeScriptFile < SourceFile
|
11
11
|
# @param [Compiler::CompilationContext] compilation_context
|
12
12
|
#
|
13
|
-
def process(
|
13
|
+
def process(_compilation_context)
|
14
14
|
return if destination_file_up_to_date?
|
15
15
|
|
16
16
|
write_compiled(CoffeeScript.compile(File.new(abs_source_path)))
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'css_parser'
|
4
|
+
|
5
|
+
module Epuber
|
6
|
+
class Compiler
|
7
|
+
module FileTypes
|
8
|
+
require_relative 'source_file'
|
9
|
+
|
10
|
+
class CSSFile < SourceFile
|
11
|
+
DECLARATION_TO_FILE_GROUP_MAP = {
|
12
|
+
'background' => :image,
|
13
|
+
'background-image' => :image,
|
14
|
+
'list-style' => :image,
|
15
|
+
'list-style-image' => :image,
|
16
|
+
'content' => :image,
|
17
|
+
'cursor' => :image,
|
18
|
+
'border-image' => :image,
|
19
|
+
'border-image-source' => :image,
|
20
|
+
'mask-image' => :image,
|
21
|
+
'mask-box-image' => :image,
|
22
|
+
'src' => :font,
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
URL_REGEXP = /url\((.+)\)/.freeze
|
26
|
+
|
27
|
+
# @param [Compiler::CompilationContext] compilation_context
|
28
|
+
#
|
29
|
+
def process(compilation_context)
|
30
|
+
return if destination_file_up_to_date?
|
31
|
+
|
32
|
+
write_processed(process_css(File.read(abs_source_path), compilation_context))
|
33
|
+
end
|
34
|
+
|
35
|
+
# Processes CSS file, resolves all linked files and adds them to file resolver
|
36
|
+
#
|
37
|
+
# @param [String] content
|
38
|
+
# @param [Compiler::CompilationContext] compilation_context
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
#
|
42
|
+
def process_css(content, compilation_context)
|
43
|
+
file_resolver = compilation_context.file_resolver
|
44
|
+
|
45
|
+
parser = UI.print_step_processing_time('css parsing') do
|
46
|
+
parser = CssParser::Parser.new
|
47
|
+
parser.load_string!(content)
|
48
|
+
parser
|
49
|
+
end
|
50
|
+
|
51
|
+
# resolve links to files, add other linked resources and compute correct path
|
52
|
+
UI.print_step_processing_time('resolving url()') do
|
53
|
+
parser.each_rule_set do |rule_set, _media_types|
|
54
|
+
declarations = rule_set.instance_eval { @declarations }
|
55
|
+
declarations.each do |property, decl_value|
|
56
|
+
value = decl_value.to_s
|
57
|
+
next unless value =~ URL_REGEXP
|
58
|
+
|
59
|
+
path = Regexp.last_match(1)
|
60
|
+
if path.start_with?('"') && path.end_with?('"')
|
61
|
+
path = path[1..-2]
|
62
|
+
quote = '"'
|
63
|
+
end
|
64
|
+
if path.start_with?("'") && path.end_with?("'")
|
65
|
+
path = path[1..-2]
|
66
|
+
quote = "'"
|
67
|
+
end
|
68
|
+
|
69
|
+
next if path.start_with?('data:') || path.start_with?('http://') || path.start_with?('https://')
|
70
|
+
|
71
|
+
resource_group = DECLARATION_TO_FILE_GROUP_MAP[property]
|
72
|
+
dirname = File.dirname(destination_path)
|
73
|
+
|
74
|
+
begin
|
75
|
+
new_path = file_resolver.dest_finder.find_file(path, groups: resource_group, context_path: dirname)
|
76
|
+
rescue FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError
|
77
|
+
new_path = XHTMLProcessor.resolved_link_to_file(path,
|
78
|
+
resource_group,
|
79
|
+
dirname,
|
80
|
+
file_resolver.source_finder).to_s
|
81
|
+
pkg_abs_path = File.expand_path(new_path, dirname).unicode_normalize
|
82
|
+
pkg_new_path = Pathname.new(pkg_abs_path)
|
83
|
+
.relative_path_from(Pathname.new(file_resolver.source_path))
|
84
|
+
.to_s
|
85
|
+
|
86
|
+
file_class = FileResolver.file_class_for(File.extname(new_path))
|
87
|
+
file = file_class.new(pkg_new_path)
|
88
|
+
file.path_type = :manifest
|
89
|
+
file_resolver.add_file(file)
|
90
|
+
end
|
91
|
+
|
92
|
+
new_url = FileResolver.renamed_file_with_path(new_path)
|
93
|
+
content = content.gsub(value, "url(#{quote}#{new_url}#{quote})")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
content
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -16,14 +16,16 @@ module Epuber
|
|
16
16
|
dest = final_destination_path
|
17
17
|
source = abs_source_path
|
18
18
|
|
19
|
-
img = Magick::Image
|
19
|
+
img = Magick::Image.read(source).first
|
20
20
|
|
21
21
|
resolution = img.columns * img.rows
|
22
22
|
max_resolution = 3_000_000
|
23
23
|
|
24
24
|
if resolution > max_resolution
|
25
25
|
img = img.change_geometry("#{max_resolution}@>") do |width, height, b_img|
|
26
|
-
UI.print_processing_debug_info(
|
26
|
+
UI.print_processing_debug_info(<<~MSG)
|
27
|
+
downscaling from resolution #{b_img.columns}x#{b_img.rows} to #{width}x#{height}
|
28
|
+
MSG
|
27
29
|
b_img.resize!(width, height)
|
28
30
|
end
|
29
31
|
|