epuber 0.3.12 → 0.4.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/README.md +1 -1
- data/epuber.gemspec +13 -15
- data/lib/epuber/book.rb +35 -4
- data/lib/epuber/book/target.rb +41 -2
- data/lib/epuber/command.rb +5 -0
- data/lib/epuber/command/build.rb +155 -0
- data/lib/epuber/command/compile.rb +5 -131
- data/lib/epuber/command/init.rb +19 -7
- data/lib/epuber/command/server.rb +1 -1
- data/lib/epuber/compiler.rb +56 -2
- data/lib/epuber/compiler/compilation_context.rb +27 -1
- data/lib/epuber/compiler/file_database.rb +138 -0
- data/lib/epuber/compiler/file_finders/abstract.rb +3 -1
- data/lib/epuber/compiler/file_resolver.rb +6 -2
- data/lib/epuber/compiler/file_stat.rb +76 -0
- data/lib/epuber/compiler/file_types/abstract_file.rb +3 -37
- data/lib/epuber/compiler/file_types/bade_file.rb +63 -4
- data/lib/epuber/compiler/file_types/coffee_script_file.rb +24 -0
- data/lib/epuber/compiler/file_types/generated_file.rb +3 -1
- data/lib/epuber/compiler/file_types/image_file.rb +6 -4
- data/lib/epuber/compiler/file_types/source_file.rb +63 -1
- data/lib/epuber/compiler/file_types/static_file.rb +2 -2
- data/lib/epuber/compiler/file_types/stylus_file.rb +15 -0
- data/lib/epuber/compiler/file_types/xhtml_file.rb +71 -13
- data/lib/epuber/compiler/opf_generator.rb +1 -0
- data/lib/epuber/compiler/xhtml_processor.rb +94 -17
- data/lib/epuber/config.rb +47 -14
- data/lib/epuber/plugin.rb +6 -1
- data/lib/epuber/server.rb +35 -11
- data/lib/epuber/server/handlers.rb +2 -1
- data/lib/epuber/server/pages/book.bade +6 -2
- data/lib/epuber/server/pages/common.bade +5 -7
- data/lib/epuber/user_interface.rb +36 -4
- data/lib/epuber/vendor/version.rb +2 -2
- data/lib/epuber/version.rb +3 -1
- metadata +49 -57
data/lib/epuber/command/init.rb
CHANGED
@@ -36,12 +36,12 @@ module Epuber
|
|
36
36
|
write_bookspec(@book_name)
|
37
37
|
write_sublime_project(@book_name)
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
create_folder('images')
|
40
|
+
create_folder('fonts')
|
41
|
+
create_folder('styles')
|
42
42
|
write_default_style(@book_name)
|
43
43
|
|
44
|
-
|
44
|
+
create_folder('text')
|
45
45
|
|
46
46
|
print_good_bye(@book_name)
|
47
47
|
end
|
@@ -51,7 +51,7 @@ module Epuber
|
|
51
51
|
|
52
52
|
def print_good_bye(book_id)
|
53
53
|
puts <<-END.ansi.green
|
54
|
-
Project initialized, please review #{book_id}.bookspec file, remove comments.
|
54
|
+
Project initialized, please review #{book_id}.bookspec file, remove comments and fill some attributes like book title.
|
55
55
|
END
|
56
56
|
end
|
57
57
|
|
@@ -101,8 +101,10 @@ END
|
|
101
101
|
*.epub
|
102
102
|
*.mobi
|
103
103
|
!.epuber/
|
104
|
-
.epuber/build
|
105
|
-
.epuber/release_build
|
104
|
+
.epuber/build/
|
105
|
+
.epuber/release_build/
|
106
|
+
.epuber/build_cache/
|
107
|
+
.epuber/metadata/
|
106
108
|
|
107
109
|
END
|
108
110
|
)
|
@@ -122,6 +124,16 @@ END
|
|
122
124
|
#
|
123
125
|
def write(file_path, string)
|
124
126
|
File.write(file_path, string)
|
127
|
+
puts " #{'create'.ansi.green} #{file_path}"
|
128
|
+
end
|
129
|
+
|
130
|
+
# @param [String] dir_path path to dir
|
131
|
+
#
|
132
|
+
# @return [nil]
|
133
|
+
#
|
134
|
+
def create_folder(dir_path)
|
135
|
+
FileUtils.mkdir_p(dir_path)
|
136
|
+
puts " #{'create'.ansi.green} #{dir_path}/"
|
125
137
|
end
|
126
138
|
|
127
139
|
# @param text [String]
|
data/lib/epuber/compiler.rb
CHANGED
@@ -24,6 +24,8 @@ module Epuber
|
|
24
24
|
require_relative 'compiler/file_resolver'
|
25
25
|
require_relative 'compiler/file_finders/normal'
|
26
26
|
|
27
|
+
require_relative 'compiler/file_database'
|
28
|
+
|
27
29
|
|
28
30
|
EPUB_CONTENT_FOLDER = 'OEBPS'
|
29
31
|
|
@@ -59,13 +61,14 @@ module Epuber
|
|
59
61
|
#
|
60
62
|
# @return [void]
|
61
63
|
#
|
62
|
-
def compile(build_folder, check: false, write: false, release: false, verbose: false)
|
64
|
+
def compile(build_folder, check: false, write: false, release: false, verbose: false, use_cache: true)
|
63
65
|
@file_resolver = FileResolver.new(Config.instance.project_path, build_folder)
|
64
66
|
compilation_context.file_resolver = @file_resolver
|
65
67
|
compilation_context.should_check = check
|
66
68
|
compilation_context.should_write = write
|
67
69
|
compilation_context.release_build = release
|
68
70
|
compilation_context.verbose = verbose
|
71
|
+
compilation_context.use_cache = use_cache
|
69
72
|
|
70
73
|
self.class.globals_catcher.catch do
|
71
74
|
@build_folder = build_folder
|
@@ -74,6 +77,9 @@ module Epuber
|
|
74
77
|
|
75
78
|
puts " handling target #{@target.name.inspect} in build dir `#{Config.instance.pretty_path_from_project(build_folder)}`"
|
76
79
|
|
80
|
+
file_resolver.add_file(FileTypes::SourceFile.new(Config.instance.pretty_path_from_project(@book.file_path).to_s))
|
81
|
+
compilation_context.plugins
|
82
|
+
|
77
83
|
parse_toc_item(@target.root_toc)
|
78
84
|
parse_target_file_requests
|
79
85
|
|
@@ -83,6 +89,15 @@ module Epuber
|
|
83
89
|
# build folder cleanup
|
84
90
|
remove_unnecessary_files
|
85
91
|
remove_empty_folders
|
92
|
+
|
93
|
+
source_paths = file_resolver.files.select { |a| a.is_a?(FileTypes::SourceFile) }.map { |a| a.source_path }
|
94
|
+
compilation_context.source_file_database.cleanup(source_paths)
|
95
|
+
compilation_context.source_file_database.update_all_metadata
|
96
|
+
compilation_context.source_file_database.save_to_file
|
97
|
+
|
98
|
+
compilation_context.target_file_database.cleanup(source_paths)
|
99
|
+
compilation_context.target_file_database.update_all_metadata
|
100
|
+
compilation_context.target_file_database.save_to_file
|
86
101
|
end
|
87
102
|
ensure
|
88
103
|
self.class.globals_catcher.clear_all
|
@@ -205,10 +220,49 @@ module Epuber
|
|
205
220
|
end
|
206
221
|
end
|
207
222
|
|
208
|
-
# @param [FileTypes::AbstractFile] file
|
223
|
+
# @param [Epuber::Compiler::FileTypes::AbstractFile] file
|
209
224
|
#
|
210
225
|
def process_file(file)
|
226
|
+
file.compilation_context = compilation_context
|
227
|
+
|
228
|
+
resolve_dependencies(file) if file.is_a?(FileTypes::SourceFile)
|
211
229
|
file.process(compilation_context)
|
230
|
+
|
231
|
+
file.compilation_context = nil
|
232
|
+
end
|
233
|
+
|
234
|
+
# @param [FileTypes::SourceFile] file
|
235
|
+
#
|
236
|
+
def resolve_dependencies(file)
|
237
|
+
deps = file.find_dependencies
|
238
|
+
|
239
|
+
# compute better paths for FileDatabase
|
240
|
+
dirname = File.dirname(file.source_path)
|
241
|
+
paths = deps.map { |relative| Config.instance.pretty_path_from_project(File.expand_path(relative, dirname)) }.uniq
|
242
|
+
|
243
|
+
# add missing files to file_resolver
|
244
|
+
paths.each do |path|
|
245
|
+
next if file_resolver.file_with_source_path(path)
|
246
|
+
file_resolver.add_file(FileTypes::SourceFile.new(path))
|
247
|
+
end
|
248
|
+
|
249
|
+
# add .bookspec file
|
250
|
+
paths += [Config.instance.pretty_path_from_project(@book.file_path).to_s]
|
251
|
+
|
252
|
+
# add all activated plugin files
|
253
|
+
paths += compilation_context.plugins.map do |plugin|
|
254
|
+
plugin.files.map { |p_file| p_file.source_path }
|
255
|
+
end.flatten
|
256
|
+
|
257
|
+
# add dependencies to databases
|
258
|
+
source_db = compilation_context.source_file_database
|
259
|
+
source_db.update_metadata(file.source_path) unless source_db.file_stat_for(file.source_path)
|
260
|
+
source_db.add_dependency(paths, to: file.source_path)
|
261
|
+
|
262
|
+
# add dependencies to databases
|
263
|
+
target_db = compilation_context.target_file_database
|
264
|
+
target_db.update_metadata(file.source_path) unless target_db.file_stat_for(file.source_path)
|
265
|
+
target_db.add_dependency(paths, to: file.source_path)
|
212
266
|
end
|
213
267
|
|
214
268
|
# @return nil
|
@@ -16,12 +16,24 @@ module Epuber
|
|
16
16
|
#
|
17
17
|
attr_accessor :file_resolver
|
18
18
|
|
19
|
+
# This will track source files regardless of current target
|
20
|
+
#
|
21
|
+
# @return [Epuber::Compiler::FileDatabase]
|
22
|
+
#
|
23
|
+
attr_reader :source_file_database
|
24
|
+
|
25
|
+
# This will track source files depend on current target
|
26
|
+
#
|
27
|
+
# @return [Epuber::Compiler::FileDatabase]
|
28
|
+
#
|
29
|
+
attr_reader :target_file_database
|
30
|
+
|
19
31
|
# @return [Array<Epuber::Plugin>]
|
20
32
|
#
|
21
33
|
def plugins
|
22
34
|
@plugins ||= @target.plugins.map do |path|
|
23
35
|
begin
|
24
|
-
plugin = Plugin.new(
|
36
|
+
plugin = Plugin.new(path)
|
25
37
|
plugin.files.each do |file|
|
26
38
|
file_resolver.add_file(file)
|
27
39
|
end
|
@@ -68,6 +80,10 @@ module Epuber
|
|
68
80
|
#
|
69
81
|
attr_accessor :release_build
|
70
82
|
|
83
|
+
# @return [Bool]
|
84
|
+
#
|
85
|
+
attr_accessor :use_cache
|
86
|
+
|
71
87
|
# @return [Bool]
|
72
88
|
#
|
73
89
|
attr_accessor :verbose
|
@@ -76,10 +92,20 @@ module Epuber
|
|
76
92
|
verbose
|
77
93
|
end
|
78
94
|
|
95
|
+
def debug?
|
96
|
+
!release_build
|
97
|
+
end
|
98
|
+
|
99
|
+
def incremental_build?
|
100
|
+
use_cache
|
101
|
+
end
|
79
102
|
|
80
103
|
def initialize(book, target)
|
81
104
|
@book = book
|
82
105
|
@target = target
|
106
|
+
|
107
|
+
@source_file_database = FileDatabase.new(Config.instance.file_stat_database_path)
|
108
|
+
@target_file_database = FileDatabase.new(Config.instance.target_file_stat_database_path(target))
|
83
109
|
end
|
84
110
|
end
|
85
111
|
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
|
7
|
+
module Epuber
|
8
|
+
class Compiler
|
9
|
+
require_relative 'file_stat'
|
10
|
+
|
11
|
+
class FileDatabase
|
12
|
+
|
13
|
+
# @return [Hash<String, Epuber::Compiler::FileStat>]
|
14
|
+
#
|
15
|
+
attr_accessor :all_files
|
16
|
+
|
17
|
+
# @return [String]
|
18
|
+
#
|
19
|
+
attr_reader :store_file_path
|
20
|
+
|
21
|
+
# @param [String] path
|
22
|
+
#
|
23
|
+
def initialize(path)
|
24
|
+
@store_file_path = path
|
25
|
+
@all_files = YAML.load_file(path) || {}
|
26
|
+
rescue
|
27
|
+
@all_files = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [String] file_path
|
31
|
+
#
|
32
|
+
def changed?(file_path, transitive: true, default_value: true)
|
33
|
+
stat = @all_files[file_path]
|
34
|
+
return default_value if stat.nil?
|
35
|
+
|
36
|
+
result = (stat != FileStat.new(file_path))
|
37
|
+
|
38
|
+
if transitive
|
39
|
+
result ||= stat.dependency_paths.any? do |path|
|
40
|
+
changed?(path, transitive: transitive, default_value: false)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param [String] file_path
|
48
|
+
#
|
49
|
+
# @return [FileStat]
|
50
|
+
#
|
51
|
+
def file_stat_for(file_path)
|
52
|
+
@all_files[file_path]
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param [String] file_path
|
56
|
+
#
|
57
|
+
def up_to_date?(file_path, transitive: true)
|
58
|
+
!changed?(file_path, transitive: transitive)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [String] file_path
|
62
|
+
#
|
63
|
+
def update_metadata(file_path, load_stats: true)
|
64
|
+
old_stat = @all_files[file_path]
|
65
|
+
old_dependencies = old_stat ? old_stat.dependency_paths : []
|
66
|
+
|
67
|
+
@all_files[file_path] = FileStat.new(file_path, load_stats: load_stats, dependency_paths: old_dependencies)
|
68
|
+
end
|
69
|
+
|
70
|
+
def update_all_metadata
|
71
|
+
@all_files.each do |file_path, _|
|
72
|
+
update_metadata(file_path)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param [Array<String>, String] file_path path to file that will be dependent on
|
77
|
+
# @param [String] to path to original file, that will has new dependency
|
78
|
+
#
|
79
|
+
def add_dependency(file_path, to: nil)
|
80
|
+
raise ArgumentError, ':to is required' if to.nil?
|
81
|
+
|
82
|
+
file_paths = Array(file_path)
|
83
|
+
|
84
|
+
to_stat = @all_files[to]
|
85
|
+
raise ArgumentError, ":to (#{to}) file is not in database" if to_stat.nil?
|
86
|
+
|
87
|
+
to_stat.add_dependency!(file_paths)
|
88
|
+
|
89
|
+
begin
|
90
|
+
file_paths.each do |path|
|
91
|
+
update_metadata(path, load_stats: false) if @all_files[path].nil?
|
92
|
+
end
|
93
|
+
rescue Errno::ENOENT
|
94
|
+
# no action, valid case where dependant file does not exist
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @param [Array<String>] file_paths
|
99
|
+
#
|
100
|
+
def cleanup(file_paths)
|
101
|
+
to_remove = @all_files.keys - file_paths
|
102
|
+
to_remove.each { |key| @all_files.delete(key) }
|
103
|
+
|
104
|
+
@all_files.each do |_, stat|
|
105
|
+
_cleanup_stat_dependency_list(file_paths, stat)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param [String] path
|
110
|
+
#
|
111
|
+
def save_to_file(path = store_file_path)
|
112
|
+
FileUtils.mkdir_p(File.dirname(path))
|
113
|
+
|
114
|
+
File.write(path, @all_files.to_yaml)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# @param [Array<String>] file_paths
|
123
|
+
# @param [FileStat] stat
|
124
|
+
#
|
125
|
+
def _cleanup_stat_dependency_list(file_paths, stat)
|
126
|
+
stat.keep_dependencies!(file_paths)
|
127
|
+
|
128
|
+
stat.dependency_paths.each do |path|
|
129
|
+
next_stat = @all_files[path]
|
130
|
+
next if next_stat.nil?
|
131
|
+
|
132
|
+
_cleanup_stat_dependency_list(file_paths, next_stat)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -69,13 +69,15 @@ module Epuber
|
|
69
69
|
image: %w(.png .jpg .jpeg),
|
70
70
|
font: %w(.otf .ttf),
|
71
71
|
style: %w(.css .styl),
|
72
|
-
script: %w(.js),
|
72
|
+
script: %w(.js .coffee),
|
73
73
|
}
|
74
74
|
|
75
75
|
EXTENSIONS_RENAME = {
|
76
76
|
'.styl' => '.css',
|
77
77
|
|
78
78
|
'.bade' => '.xhtml',
|
79
|
+
|
80
|
+
'.coffee' => '.js',
|
79
81
|
}
|
80
82
|
|
81
83
|
class Abstract
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require_relative '../ruby_extensions/string'
|
4
|
+
require 'active_support/core_ext/object/try'
|
4
5
|
|
5
6
|
module Epuber
|
6
7
|
class Compiler
|
@@ -19,6 +20,7 @@ module Epuber
|
|
19
20
|
require_relative 'file_types/mime_type_file'
|
20
21
|
require_relative 'file_types/container_xml_file'
|
21
22
|
require_relative 'file_types/ibooks_display_options_file'
|
23
|
+
require_relative 'file_types/coffee_script_file'
|
22
24
|
|
23
25
|
|
24
26
|
class FileResolver
|
@@ -242,7 +244,7 @@ module Epuber
|
|
242
244
|
#
|
243
245
|
# @return [String] path with changed extension
|
244
246
|
#
|
245
|
-
def renamed_file_with_path(path)
|
247
|
+
def self.renamed_file_with_path(path)
|
246
248
|
extname = File.extname(path)
|
247
249
|
new_extname = FileFinders::EXTENSIONS_RENAME[extname]
|
248
250
|
|
@@ -261,7 +263,7 @@ module Epuber
|
|
261
263
|
if file.final_destination_path.nil?
|
262
264
|
dest_path = if file.respond_to?(:source_path) && !file.source_path.nil?
|
263
265
|
file.abs_source_path = File.expand_path(file.source_path, source_path)
|
264
|
-
renamed_file_with_path(file.source_path)
|
266
|
+
self.class.renamed_file_with_path(file.source_path)
|
265
267
|
elsif !file.destination_path.nil?
|
266
268
|
file.destination_path
|
267
269
|
else
|
@@ -282,6 +284,8 @@ module Epuber
|
|
282
284
|
mapping = {
|
283
285
|
'.styl' => FileTypes::StylusFile,
|
284
286
|
|
287
|
+
'.coffee' => FileTypes::CoffeeScriptFile,
|
288
|
+
|
285
289
|
'.bade' => FileTypes::BadeFile,
|
286
290
|
'.xhtml' => FileTypes::XHTMLFile,
|
287
291
|
'.html' => FileTypes::XHTMLFile,
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Epuber
|
5
|
+
class Compiler
|
6
|
+
class FileStat
|
7
|
+
# @return [Date]
|
8
|
+
#
|
9
|
+
attr_reader :mtime
|
10
|
+
|
11
|
+
# @return [Date]
|
12
|
+
#
|
13
|
+
attr_reader :ctime
|
14
|
+
|
15
|
+
# @return [Fixnum]
|
16
|
+
#
|
17
|
+
attr_reader :size
|
18
|
+
|
19
|
+
# @return [String]
|
20
|
+
#
|
21
|
+
attr_reader :file_path
|
22
|
+
|
23
|
+
# @return [String]
|
24
|
+
#
|
25
|
+
attr_reader :dependency_paths
|
26
|
+
|
27
|
+
# @param [String] path
|
28
|
+
# @param [File::Stat] stat
|
29
|
+
# @param [Bool] load_stats
|
30
|
+
#
|
31
|
+
def initialize(path, stat = nil, load_stats: true, dependency_paths: [])
|
32
|
+
@file_path = path
|
33
|
+
|
34
|
+
if load_stats
|
35
|
+
begin
|
36
|
+
stat ||= File.stat(path)
|
37
|
+
@mtime = stat.mtime
|
38
|
+
@ctime = stat.ctime
|
39
|
+
@size = stat.size
|
40
|
+
rescue
|
41
|
+
# noop
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
@dependency_paths = dependency_paths
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Array<String>, String] path
|
49
|
+
#
|
50
|
+
def add_dependency!(path)
|
51
|
+
@dependency_paths += Array(path)
|
52
|
+
@dependency_paths.uniq!
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param [Array<String>] paths
|
56
|
+
#
|
57
|
+
def keep_dependencies!(paths)
|
58
|
+
to_delete = (dependency_paths - paths)
|
59
|
+
@dependency_paths -= to_delete
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param [FileStat] other
|
63
|
+
#
|
64
|
+
# @return [Bool]
|
65
|
+
#
|
66
|
+
def ==(other)
|
67
|
+
raise AttributeError, "other must be class of #{self.class}" unless other.is_a?(FileStat)
|
68
|
+
|
69
|
+
file_path == other.file_path &&
|
70
|
+
size == other.size &&
|
71
|
+
mtime == other.mtime &&
|
72
|
+
ctime == other.ctime
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|