epuber 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/bin/epuber +10 -0
- data/epuber.gemspec +51 -0
- data/lib/epuber.rb +14 -0
- data/lib/epuber/book.rb +267 -0
- data/lib/epuber/book/contributor.rb +90 -0
- data/lib/epuber/book/file_request.rb +68 -0
- data/lib/epuber/book/target.rb +284 -0
- data/lib/epuber/book/toc_item.rb +130 -0
- data/lib/epuber/checker.rb +19 -0
- data/lib/epuber/checker/text_checker.rb +129 -0
- data/lib/epuber/checker_transformer_base.rb +71 -0
- data/lib/epuber/command.rb +65 -0
- data/lib/epuber/command/compile.rb +142 -0
- data/lib/epuber/command/init.rb +145 -0
- data/lib/epuber/command/server.rb +59 -0
- data/lib/epuber/compiler.rb +255 -0
- data/lib/epuber/compiler/compilation_context.rb +86 -0
- data/lib/epuber/compiler/file_finders/abstract.rb +270 -0
- data/lib/epuber/compiler/file_finders/imaginary.rb +167 -0
- data/lib/epuber/compiler/file_finders/normal.rb +22 -0
- data/lib/epuber/compiler/file_resolver.rb +316 -0
- data/lib/epuber/compiler/file_types/abstract_file.rb +119 -0
- data/lib/epuber/compiler/file_types/bade_file.rb +39 -0
- data/lib/epuber/compiler/file_types/container_xml_file.rb +26 -0
- data/lib/epuber/compiler/file_types/generated_file.rb +29 -0
- data/lib/epuber/compiler/file_types/ibooks_display_options_file.rb +31 -0
- data/lib/epuber/compiler/file_types/image_file.rb +42 -0
- data/lib/epuber/compiler/file_types/mime_type_file.rb +20 -0
- data/lib/epuber/compiler/file_types/nav_file.rb +42 -0
- data/lib/epuber/compiler/file_types/opf_file.rb +27 -0
- data/lib/epuber/compiler/file_types/source_file.rb +52 -0
- data/lib/epuber/compiler/file_types/static_file.rb +18 -0
- data/lib/epuber/compiler/file_types/stylus_file.rb +20 -0
- data/lib/epuber/compiler/file_types/xhtml_file.rb +102 -0
- data/lib/epuber/compiler/generator.rb +67 -0
- data/lib/epuber/compiler/meta_inf_generator.rb +41 -0
- data/lib/epuber/compiler/nav_generator.rb +201 -0
- data/lib/epuber/compiler/opf_generator.rb +284 -0
- data/lib/epuber/compiler/xhtml_processor.rb +254 -0
- data/lib/epuber/config.rb +133 -0
- data/lib/epuber/dsl/attribute.rb +248 -0
- data/lib/epuber/dsl/attribute_support.rb +130 -0
- data/lib/epuber/dsl/object.rb +145 -0
- data/lib/epuber/dsl/tree_object.rb +101 -0
- data/lib/epuber/helper.rb +19 -0
- data/lib/epuber/lockfile.rb +55 -0
- data/lib/epuber/plugin.rb +108 -0
- data/lib/epuber/ruby_extensions/match_data.rb +40 -0
- data/lib/epuber/ruby_extensions/thread.rb +13 -0
- data/lib/epuber/server.rb +614 -0
- data/lib/epuber/server/auto_refresh/auto_refresh.coffee +97 -0
- data/lib/epuber/server/auto_refresh/connector.coffee +125 -0
- data/lib/epuber/server/auto_refresh/protocol.coffee +41 -0
- data/lib/epuber/server/auto_refresh/reloader.coffee +261 -0
- data/lib/epuber/server/base.styl +3 -0
- data/lib/epuber/server/basic.styl +211 -0
- data/lib/epuber/server/book_content.styl +39 -0
- data/lib/epuber/server/default_cover.png +0 -0
- 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
- data/lib/epuber/server/handlers.rb +67 -0
- data/lib/epuber/server/keyboard_control.coffee +6 -0
- data/lib/epuber/server/pages/book.bade +75 -0
- data/lib/epuber/server/pages/common.bade +59 -0
- data/lib/epuber/server/pages/files.bade +17 -0
- data/lib/epuber/server/pages/toc.bade +29 -0
- data/lib/epuber/server/support.coffee +10 -0
- data/lib/epuber/templates/template.bookspec +143 -0
- data/lib/epuber/third_party/bower.rb +22 -0
- data/lib/epuber/third_party/bower/bower.json +10 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/bower.json +10 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.d.ts +33 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.js +173 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.min.js +6 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/MIT-LICENSE.txt +21 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/bower.json +28 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.js +9210 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.map +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax.js +786 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/jsonp.js +89 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/load.js +75 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseJSON.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseXML.js +28 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/script.js +64 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/nonce.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/rquery.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/xhr.js +136 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes.js +11 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/attr.js +141 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/classes.js +158 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/prop.js +94 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/support.js +35 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/val.js +161 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/callbacks.js +205 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core.js +502 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/access.js +60 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/init.js +123 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/parseHTML.js +39 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/ready.js +97 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/var/rsingleTag.js +4 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css.js +450 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/addGetHookIf.js +22 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/curCSS.js +57 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/defaultDisplay.js +70 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/hiddenVisibleSelectors.js +15 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/support.js +96 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/swap.js +28 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/cssExpand.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/getStyles.js +12 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/isHidden.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rmargin.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rnumnonpx.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data.js +178 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/Data.js +181 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/accepts.js +20 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_priv.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_user.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/deferred.js +149 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/deprecated.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/dimensions.js +50 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/effects.js +648 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/Tween.js +114 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/animatedSelector.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event.js +868 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event/ajax.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event/alias.js +39 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event/support.js +9 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/amd.js +24 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/global.js +32 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/intro.js +44 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/jquery.js +37 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation.js +580 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/_evalUrl.js +18 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/support.js +32 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/var/rcheckableType.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/offset.js +207 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/outro.js +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/queue.js +142 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/queue/delay.js +22 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-native.js +172 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-sizzle.js +14 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/selector.js +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/serialize.js +111 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.js +2067 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.map +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing.js +199 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/findFilter.js +100 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/var/rneedsContext.js +6 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/arr.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/class2type.js +4 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/concat.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/hasOwn.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/indexOf.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/pnum.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/push.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/rnotwhite.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/slice.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/strundefined.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/support.js +4 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/toString.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/wrap.js +79 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/MIT-LICENSE +20 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/Makefile +4 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/README.markdown +212 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/bower.json +27 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/keymaster.js +296 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/package.json +11 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/test.html +93 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/LICENSE.txt +21 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/README.md +21 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/bower.json +18 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/jquery.spin.js +80 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/spin.js +337 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/LICENSE.txt +21 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/README.md +534 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/bower.json +16 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/contributing.md +19 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/IPv6.js +188 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/SecondLevelDomains.js +241 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentQuery.js +104 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentURI.js +97 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.js +2115 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.min.js +86 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URITemplate.js +499 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.js +235 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.min.js +7 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/punycode.js +508 -0
- data/lib/epuber/transformer.rb +19 -0
- data/lib/epuber/transformer/text_transformer.rb +60 -0
- data/lib/epuber/user_interface.rb +186 -0
- data/lib/epuber/vendor/globals_context.rb +26 -0
- data/lib/epuber/vendor/hash_binding.rb +26 -0
- data/lib/epuber/vendor/nokogiri_extensions.rb +30 -0
- data/lib/epuber/vendor/ruby_templater.rb +71 -0
- data/lib/epuber/vendor/size.rb +20 -0
- data/lib/epuber/vendor/version.rb +83 -0
- data/lib/epuber/version.rb +4 -0
- metadata +556 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
require 'os'
|
5
|
+
|
6
|
+
|
7
|
+
module Epuber
|
8
|
+
class Command
|
9
|
+
class Server < Command
|
10
|
+
self.summary = 'Starts web server to display and debug e-book pages.'
|
11
|
+
self.arguments = [
|
12
|
+
CLAide::Argument.new('TARGET', false, false),
|
13
|
+
]
|
14
|
+
|
15
|
+
def self.options
|
16
|
+
[
|
17
|
+
['--open', 'Opens the web page in default web browser, working only on OS X'],
|
18
|
+
].concat(super)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param args [CLAide::ARGV]
|
22
|
+
#
|
23
|
+
def initialize(args)
|
24
|
+
super
|
25
|
+
@selected_target_name = args.shift_argument
|
26
|
+
@open_web_browser = args.flag?('open', false)
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
super
|
31
|
+
verify_one_bookspec_exists!
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
super
|
36
|
+
|
37
|
+
require_relative '../server'
|
38
|
+
|
39
|
+
target = if @selected_target_name.nil?
|
40
|
+
book.targets.first
|
41
|
+
else
|
42
|
+
book.target_named(@selected_target_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
help!('Not existing target') if target.nil?
|
46
|
+
|
47
|
+
Epuber::Server.run!(book, target, verbose: verbose?) do |uri|
|
48
|
+
if OS.osx?
|
49
|
+
if @open_web_browser
|
50
|
+
system "open #{uri}"
|
51
|
+
else
|
52
|
+
puts 'Web browser can be automatically opened by adding --open flag, see --help'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
require 'zip'
|
9
|
+
|
10
|
+
require_relative 'vendor/nokogiri_extensions'
|
11
|
+
require_relative 'vendor/globals_context'
|
12
|
+
|
13
|
+
require_relative 'plugin'
|
14
|
+
|
15
|
+
|
16
|
+
module Epuber
|
17
|
+
class Compiler
|
18
|
+
require_relative 'compiler/compilation_context'
|
19
|
+
|
20
|
+
require_relative 'compiler/opf_generator'
|
21
|
+
require_relative 'compiler/nav_generator'
|
22
|
+
require_relative 'compiler/meta_inf_generator'
|
23
|
+
|
24
|
+
require_relative 'compiler/file_resolver'
|
25
|
+
require_relative 'compiler/file_finders/normal'
|
26
|
+
|
27
|
+
|
28
|
+
EPUB_CONTENT_FOLDER = 'OEBPS'
|
29
|
+
|
30
|
+
# @return [Epuber::GlobalsContext]
|
31
|
+
#
|
32
|
+
def self.globals_catcher
|
33
|
+
@globals_catcher ||= Epuber::GlobalsContext.new
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Epuber::Compiler::FileResolver]
|
37
|
+
#
|
38
|
+
attr_reader :file_resolver
|
39
|
+
|
40
|
+
# @return [CompilationContext]
|
41
|
+
#
|
42
|
+
attr_reader :compilation_context
|
43
|
+
|
44
|
+
# @param book [Epuber::Book::Book]
|
45
|
+
# @param target [Epuber::Book::Target]
|
46
|
+
#
|
47
|
+
def initialize(book, target)
|
48
|
+
@book = book
|
49
|
+
@target = target
|
50
|
+
@compilation_context = CompilationContext.new(book, target)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Compile target to build folder
|
54
|
+
#
|
55
|
+
# @param build_folder [String] path to folder, where will be stored all compiled files
|
56
|
+
# @param [Bool] check should run non-release checkers
|
57
|
+
# @param [Bool] write should perform transformations of source files and write them back
|
58
|
+
# @param [Bool] release this is release build
|
59
|
+
#
|
60
|
+
# @return [void]
|
61
|
+
#
|
62
|
+
def compile(build_folder, check: false, write: false, release: false, verbose: false)
|
63
|
+
@file_resolver = FileResolver.new(Config.instance.project_path, build_folder)
|
64
|
+
compilation_context.file_resolver = @file_resolver
|
65
|
+
compilation_context.should_check = check
|
66
|
+
compilation_context.should_write = write
|
67
|
+
compilation_context.release_build = release
|
68
|
+
compilation_context.verbose = verbose
|
69
|
+
|
70
|
+
self.class.globals_catcher.catch do
|
71
|
+
@build_folder = build_folder
|
72
|
+
|
73
|
+
FileUtils.mkdir_p(build_folder)
|
74
|
+
|
75
|
+
puts " handling target #{@target.name.inspect} in build dir `#{Config.instance.pretty_path_from_project(build_folder)}`"
|
76
|
+
|
77
|
+
parse_toc_item(@target.root_toc)
|
78
|
+
parse_target_file_requests
|
79
|
+
|
80
|
+
process_all_target_files
|
81
|
+
generate_other_files
|
82
|
+
|
83
|
+
# build folder cleanup
|
84
|
+
remove_unnecessary_files
|
85
|
+
remove_empty_folders
|
86
|
+
end
|
87
|
+
ensure
|
88
|
+
self.class.globals_catcher.clear_all
|
89
|
+
end
|
90
|
+
|
91
|
+
# Archives current target files to epub
|
92
|
+
#
|
93
|
+
# @param path [String] path to created archive
|
94
|
+
#
|
95
|
+
# @return [String] path
|
96
|
+
#
|
97
|
+
def archive(path = nil, configuration_suffix: nil)
|
98
|
+
path ||= epub_name(configuration_suffix)
|
99
|
+
|
100
|
+
epub_path = File.expand_path(path)
|
101
|
+
|
102
|
+
Dir.chdir(@file_resolver.destination_path) do
|
103
|
+
new_paths = @file_resolver.package_files.map(&:pkg_destination_path)
|
104
|
+
|
105
|
+
if ::File.exists?(epub_path)
|
106
|
+
Zip::File.open(epub_path, true) do |zip_file|
|
107
|
+
old_paths = zip_file.instance_eval { @entry_set.entries.map(&:name) }
|
108
|
+
diff = old_paths - new_paths
|
109
|
+
diff.each do |file_to_remove|
|
110
|
+
puts "DEBUG: removing file from result EPUB: #{file_to_remove}" if compilation_context.verbose?
|
111
|
+
zip_file.remove(file_to_remove)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
run_command(%(zip -q0X "#{epub_path}" mimetype))
|
117
|
+
run_command(%(zip -qXr9D "#{epub_path}" "#{new_paths.join('" "')}" --exclude \\*.DS_Store))
|
118
|
+
end
|
119
|
+
|
120
|
+
path
|
121
|
+
end
|
122
|
+
|
123
|
+
# Creates name of epub file for current book and current target
|
124
|
+
#
|
125
|
+
# @return [String] name of result epub file
|
126
|
+
#
|
127
|
+
def epub_name(configuration_suffix = nil)
|
128
|
+
epub_name = if !@book.output_base_name.nil?
|
129
|
+
@book.output_base_name
|
130
|
+
elsif @book.from_file?
|
131
|
+
::File.basename(@book.file_path, ::File.extname(@book.file_path))
|
132
|
+
else
|
133
|
+
@book.title
|
134
|
+
end
|
135
|
+
|
136
|
+
epub_name += @book.build_version.to_s unless @book.build_version.nil?
|
137
|
+
epub_name += "-#{@target.name}" if @target != @book.default_target
|
138
|
+
epub_name += "-#{configuration_suffix}" unless configuration_suffix.nil?
|
139
|
+
epub_name + '.epub'
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# @return nil
|
146
|
+
#
|
147
|
+
def remove_empty_folders
|
148
|
+
Dir.chdir(@file_resolver.destination_path) do
|
149
|
+
Dir.glob('**/*')
|
150
|
+
.select { |d| File.directory?(d) }
|
151
|
+
.select { |d| (Dir.entries(d) - %w(. ..)).empty? }
|
152
|
+
.each do |d|
|
153
|
+
puts "DEBUG: removing empty folder `#{d}`" if compilation_context.verbose?
|
154
|
+
Dir.rmdir(d)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# @return nil
|
160
|
+
#
|
161
|
+
def remove_unnecessary_files
|
162
|
+
unnecessary_paths = @file_resolver.unneeded_files_in_destination.map { |path| File.join(@file_resolver.destination_path, path) }
|
163
|
+
unnecessary_paths.each do |path|
|
164
|
+
puts "DEBUG: removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`" if compilation_context.verbose?
|
165
|
+
|
166
|
+
File.delete(path)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return nil
|
171
|
+
#
|
172
|
+
def generate_other_files
|
173
|
+
# generate nav file (nav.xhtml or nav.ncx)
|
174
|
+
nav_file = FileTypes::NavFile.new(@target.epub_version)
|
175
|
+
@file_resolver.add_file(nav_file)
|
176
|
+
process_file(nav_file)
|
177
|
+
|
178
|
+
# generate .opf file
|
179
|
+
opf_file = FileTypes::OPFFile.new
|
180
|
+
@file_resolver.add_file(opf_file)
|
181
|
+
process_file(opf_file)
|
182
|
+
|
183
|
+
# generate mimetype file
|
184
|
+
mimetype_file = FileTypes::MimeTypeFile.new
|
185
|
+
@file_resolver.add_file(mimetype_file)
|
186
|
+
process_file(mimetype_file)
|
187
|
+
|
188
|
+
# generate META-INF files
|
189
|
+
container_xml = FileTypes::ContainerXMLFile.new
|
190
|
+
@file_resolver.add_file(container_xml)
|
191
|
+
process_file(container_xml)
|
192
|
+
|
193
|
+
if @target.epub_version >= 2.0 && @target.epub_version < 3.0
|
194
|
+
ibooks_options = FileTypes::IBooksDisplayOptionsFile.new
|
195
|
+
@file_resolver.add_file(ibooks_options)
|
196
|
+
process_file(ibooks_options)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# @return nil
|
201
|
+
#
|
202
|
+
def parse_target_file_requests
|
203
|
+
@target.files.each do |file_request|
|
204
|
+
@file_resolver.add_file_from_request(file_request)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# @param [FileTypes::AbstractFile] file
|
209
|
+
#
|
210
|
+
def process_file(file)
|
211
|
+
file.process(compilation_context)
|
212
|
+
end
|
213
|
+
|
214
|
+
# @return nil
|
215
|
+
#
|
216
|
+
def process_all_target_files
|
217
|
+
@file_resolver.manifest_files.each_with_index do |file, idx|
|
218
|
+
UI.print_processing_file(file, idx, @file_resolver.manifest_files.count)
|
219
|
+
process_file(file)
|
220
|
+
end
|
221
|
+
|
222
|
+
UI.processing_files_done
|
223
|
+
end
|
224
|
+
|
225
|
+
# @param toc_item [Epuber::Book::TocItem]
|
226
|
+
#
|
227
|
+
def parse_toc_item(toc_item)
|
228
|
+
unless toc_item.file_request.nil?
|
229
|
+
file = @file_resolver.add_file_from_request(toc_item.file_request, :spine)
|
230
|
+
file.toc_item = toc_item if file.toc_item.nil?
|
231
|
+
end
|
232
|
+
|
233
|
+
# process recursively other files
|
234
|
+
toc_item.sub_items.each do |child|
|
235
|
+
parse_toc_item(child)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# @param cmd [String]
|
240
|
+
#
|
241
|
+
# @return [void]
|
242
|
+
#
|
243
|
+
# @raise if the return value is not 0
|
244
|
+
#
|
245
|
+
def run_command(cmd)
|
246
|
+
system(cmd)
|
247
|
+
|
248
|
+
$stdout.flush
|
249
|
+
$stderr.flush
|
250
|
+
|
251
|
+
code = $CHILD_STATUS
|
252
|
+
raise 'wrong return value' if code != 0
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
module Epuber
|
5
|
+
class Compiler
|
6
|
+
class CompilationContext
|
7
|
+
# @return [Epuber::Book]
|
8
|
+
#
|
9
|
+
attr_reader :book
|
10
|
+
|
11
|
+
# @return [Epuber::Book::Target]
|
12
|
+
#
|
13
|
+
attr_reader :target
|
14
|
+
|
15
|
+
# @return [Epuber::Compiler::FileResolver]
|
16
|
+
#
|
17
|
+
attr_accessor :file_resolver
|
18
|
+
|
19
|
+
# @return [Array<Epuber::Plugin>]
|
20
|
+
#
|
21
|
+
def plugins
|
22
|
+
@plugins ||= @target.plugins.map do |path|
|
23
|
+
begin
|
24
|
+
plugin = Plugin.new(File.expand_path(path, Config.instance.project_path))
|
25
|
+
plugin.files.each do |file|
|
26
|
+
file_resolver.add_file(file)
|
27
|
+
end
|
28
|
+
plugin
|
29
|
+
rescue LoadError
|
30
|
+
UI.error "Can't find plugin at path #{path}"
|
31
|
+
end
|
32
|
+
end.compact
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [Class] klass class of thing you want to perform (Checker or Transformer)
|
36
|
+
# @param [Symbol] source_type source type of that thing (Checker or Transformer)
|
37
|
+
#
|
38
|
+
# @yield
|
39
|
+
# @yieldparam [Epuber::CheckerTransformerBase] instance of checker or transformer
|
40
|
+
#
|
41
|
+
# @return nil
|
42
|
+
#
|
43
|
+
def perform_plugin_things(klass, source_type)
|
44
|
+
plugins.each do |plugin|
|
45
|
+
plugin.instances(klass).each do |instance|
|
46
|
+
# @type [Epuber::CheckerTransformerBase] instance
|
47
|
+
|
48
|
+
next if instance.source_type != source_type
|
49
|
+
next if instance.options.include?(:run_only_before_release) && !release_build
|
50
|
+
|
51
|
+
yield instance
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
#########
|
58
|
+
|
59
|
+
# @return [Bool]
|
60
|
+
#
|
61
|
+
attr_accessor :should_check
|
62
|
+
|
63
|
+
# @return [Bool]
|
64
|
+
#
|
65
|
+
attr_accessor :should_write
|
66
|
+
|
67
|
+
# @return [Bool]
|
68
|
+
#
|
69
|
+
attr_accessor :release_build
|
70
|
+
|
71
|
+
# @return [Bool]
|
72
|
+
#
|
73
|
+
attr_accessor :verbose
|
74
|
+
|
75
|
+
def verbose?
|
76
|
+
verbose
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def initialize(book, target)
|
81
|
+
@book = book
|
82
|
+
@target = target
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'unicode_normalize'
|
4
|
+
|
5
|
+
|
6
|
+
module Epuber
|
7
|
+
class Compiler
|
8
|
+
module FileFinders
|
9
|
+
class FileNotFoundError < ::StandardError
|
10
|
+
# @return [String]
|
11
|
+
#
|
12
|
+
attr_reader :pattern
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
#
|
16
|
+
attr_reader :context_path
|
17
|
+
|
18
|
+
# @param [String] pattern
|
19
|
+
# @param [String] context_path
|
20
|
+
#
|
21
|
+
def initialize(pattern, context_path)
|
22
|
+
@pattern = pattern
|
23
|
+
@context_path = context_path
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"Not found file matching pattern `#{pattern}` from context path #{context_path}."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class MultipleFilesFoundError < ::StandardError
|
32
|
+
# @return [Array<String>]
|
33
|
+
#
|
34
|
+
attr_reader :files_paths
|
35
|
+
|
36
|
+
# @return [Array<Symbol>]
|
37
|
+
#
|
38
|
+
attr_reader :groups
|
39
|
+
|
40
|
+
# @return [String]
|
41
|
+
#
|
42
|
+
attr_reader :pattern
|
43
|
+
|
44
|
+
# @return [String]
|
45
|
+
#
|
46
|
+
attr_reader :context_path
|
47
|
+
|
48
|
+
# @param [String] pattern original pattern for searching
|
49
|
+
# @param [Symbol] groups list of groups
|
50
|
+
# @param [String] context_path context path of current searching
|
51
|
+
# @param [Array<String>] files_paths list of founded files
|
52
|
+
#
|
53
|
+
def initialize(pattern, groups, context_path, files_paths)
|
54
|
+
@pattern = pattern
|
55
|
+
@groups = groups
|
56
|
+
@context_path = context_path
|
57
|
+
@files_paths = files_paths
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
str = "Found too many files for pattern `#{pattern}` from context path #{context_path}"
|
62
|
+
str += ", file groups #{groups.map(:inspect)}" unless groups.nil?
|
63
|
+
str + ", founded files #{files_paths}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
BINARY_EXTENSIONS = %w(.png .jpeg .jpg .otf .ttf)
|
68
|
+
STATIC_EXTENSIONS = BINARY_EXTENSIONS + %w(.css .js)
|
69
|
+
|
70
|
+
GROUP_EXTENSIONS = {
|
71
|
+
text: %w(.xhtml .html .bade),
|
72
|
+
image: %w(.png .jpg .jpeg),
|
73
|
+
font: %w(.otf .ttf),
|
74
|
+
style: %w(.css .styl),
|
75
|
+
script: %w(.js),
|
76
|
+
}
|
77
|
+
|
78
|
+
EXTENSIONS_RENAME = {
|
79
|
+
'.styl' => '.css',
|
80
|
+
|
81
|
+
'.bade' => '.xhtml',
|
82
|
+
}
|
83
|
+
|
84
|
+
class Abstract
|
85
|
+
|
86
|
+
# @return [String] path where should look for source files
|
87
|
+
#
|
88
|
+
attr_reader :source_path
|
89
|
+
|
90
|
+
# @return [Array<String>]
|
91
|
+
#
|
92
|
+
attr_accessor :ignored_patterns
|
93
|
+
|
94
|
+
|
95
|
+
# @param [String] source_path
|
96
|
+
#
|
97
|
+
def initialize(source_path)
|
98
|
+
@source_path = source_path.unicode_normalize.freeze
|
99
|
+
@source_path_abs = ::File.expand_path(@source_path).unicode_normalize.freeze
|
100
|
+
@ignored_patterns = []
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def assert_one_file(files, pattern: nil, groups: nil, context_path: nil)
|
105
|
+
raise FileNotFoundError.new(pattern, context_path) if files.empty?
|
106
|
+
raise MultipleFilesFoundError.new(pattern, groups, context_path, files) if files.count >= 2
|
107
|
+
end
|
108
|
+
|
109
|
+
# Method for finding file from context_path, if there is not matching file, it will continue to search from
|
110
|
+
# #source_path and after that it tries to search recursively from #source_path.
|
111
|
+
#
|
112
|
+
# When it founds too many (more than two) it will raise MultipleFilesFoundError
|
113
|
+
#
|
114
|
+
# @param [String] pattern pattern of the desired files
|
115
|
+
# @param [Symbol] groups list of group names, nil or empty array for all groups, for valid values see GROUP_EXTENSIONS
|
116
|
+
# @param [String] context_path path for root of searching, it is also defines start folder of relative path
|
117
|
+
#
|
118
|
+
# @return [Array<String>] list of founded files
|
119
|
+
#
|
120
|
+
def find_file(pattern, groups: nil, context_path: nil, search_everywhere: true)
|
121
|
+
files = find_files(pattern, groups: groups, context_path: context_path, search_everywhere: search_everywhere)
|
122
|
+
assert_one_file(files, pattern: pattern, groups: groups, context_path: context_path)
|
123
|
+
files.first
|
124
|
+
end
|
125
|
+
|
126
|
+
# Method for finding files from context_path, if there is not matching file, it will continue to search from
|
127
|
+
# #source_path and after that it tries to search recursively from #source_path.
|
128
|
+
#
|
129
|
+
# @param [String] pattern pattern of the desired files
|
130
|
+
# @param [Array<Symbol>] groups list of group names, nil or empty array for all groups, for valid values see GROUP_EXTENSIONS
|
131
|
+
# @param [String] context_path path for root of searching, it is also defines start folder of relative path
|
132
|
+
#
|
133
|
+
# @return [Array<String>] list of founded files
|
134
|
+
#
|
135
|
+
def find_files(pattern, groups: nil, context_path: nil, search_everywhere: true)
|
136
|
+
files = []
|
137
|
+
searching_path = context_path
|
138
|
+
|
139
|
+
unless searching_path.nil?
|
140
|
+
searching_path = ::File.expand_path(context_path, @source_path_abs)
|
141
|
+
|
142
|
+
unless searching_path.start_with?(@source_path_abs)
|
143
|
+
raise "You can't search from folder (#{searching_path}) that is not sub folder of the source_path (#{source_path}) expanded to (#{@source_path_abs})."
|
144
|
+
end
|
145
|
+
|
146
|
+
files = __find_files(pattern, groups, searching_path)
|
147
|
+
end
|
148
|
+
|
149
|
+
if files.empty? && context_path != source_path
|
150
|
+
files = __find_files(pattern, groups, @source_path_abs, searching_path || @source_path_abs)
|
151
|
+
end
|
152
|
+
|
153
|
+
if files.empty? && search_everywhere && !pattern.start_with?('**')
|
154
|
+
files = __find_files('**/' + pattern, groups, @source_path_abs, searching_path || @source_path_abs)
|
155
|
+
end
|
156
|
+
|
157
|
+
files
|
158
|
+
end
|
159
|
+
|
160
|
+
# Looks for all files from #source_path recursively
|
161
|
+
#
|
162
|
+
# @param [String] pattern pattern of the desired files
|
163
|
+
# @param [Array<Symbol>] groups list of group names, nil or empty array for all groups, for valid values see GROUP_EXTENSIONS
|
164
|
+
# @param [String] context_path path for root of searching, it is also defines start folder of relative path
|
165
|
+
#
|
166
|
+
# @return [Array<String>] list of founded files
|
167
|
+
#
|
168
|
+
def find_all(pattern, groups: nil, context_path: @source_path_abs)
|
169
|
+
__find_files('**/' + pattern, groups, context_path)
|
170
|
+
end
|
171
|
+
|
172
|
+
# @param [Array<String>] paths list of file paths
|
173
|
+
# @param [Array<Symbol>] groups list of groups to filter file paths
|
174
|
+
#
|
175
|
+
# @return [Array<String>] filtered list of file paths
|
176
|
+
#
|
177
|
+
def self.group_filter_paths(paths, groups)
|
178
|
+
# filter depend on group
|
179
|
+
groups = Array(groups)
|
180
|
+
|
181
|
+
if groups.empty?
|
182
|
+
paths
|
183
|
+
else
|
184
|
+
valid_extensions = groups.map do |group|
|
185
|
+
GROUP_EXTENSIONS.fetch(group) { raise ::StandardError, "Unknown file group #{group.inspect}" }
|
186
|
+
end.flatten
|
187
|
+
|
188
|
+
paths.select do |file_path|
|
189
|
+
valid_extensions.include?(::File.extname(file_path))
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# @param [Array<String>] paths list of file paths
|
195
|
+
# @param [String] context_path
|
196
|
+
#
|
197
|
+
# @return [Array<String>] mapped list of file paths
|
198
|
+
#
|
199
|
+
def self.relative_paths_from(paths, context_path)
|
200
|
+
context_pathname = Pathname.new(context_path)
|
201
|
+
paths.map do |path|
|
202
|
+
Pathname(path.unicode_normalize).relative_path_from(context_pathname).to_s
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
# Looks for files at given context path, if there is no file, it will try again with pattern for any extension
|
209
|
+
#
|
210
|
+
# @param [String] pattern pattern of the desired files
|
211
|
+
# @param [Array<Symbol>] groups list of group names, nil or empty array for all groups, for valid values see GROUP_EXTENSIONS
|
212
|
+
# @param [String] context_path path for root of searching, it is also defines start folder of relative path
|
213
|
+
# @param [String] orig_context_path original context path, wo it will work nicely with iterative searching
|
214
|
+
#
|
215
|
+
# @return [Array<String>] list of founded files
|
216
|
+
#
|
217
|
+
def __find_files(pattern, groups, context_path, orig_context_path = context_path)
|
218
|
+
# remove fragment from pattern
|
219
|
+
pattern = pattern.sub(/#.*$/, '') if pattern.include?('#')
|
220
|
+
|
221
|
+
files = __core_find_files(pattern, groups, context_path, orig_context_path)
|
222
|
+
|
223
|
+
# try to find files with any extension
|
224
|
+
files = __core_find_files(pattern + '.*', groups, context_path, orig_context_path) if files.empty?
|
225
|
+
|
226
|
+
files
|
227
|
+
end
|
228
|
+
|
229
|
+
# Core method for finding files, it only search for files, filter by input groups and map the final paths to pretty relative paths from context_path
|
230
|
+
#
|
231
|
+
# @param [String] pattern pattern of the desired files
|
232
|
+
# @param [Array<Symbol>] groups list of group names, nil or empty array for all groups, for valid values see GROUP_EXTENSIONS
|
233
|
+
# @param [String] context_path path for root of searching, it is also defines start folder of relative path
|
234
|
+
# @param [String] orig_context_path original context path, wo it will work nicely with iterative searching
|
235
|
+
#
|
236
|
+
# @return [Array<String>] list of founded files
|
237
|
+
#
|
238
|
+
def __core_find_files(pattern, groups, context_path, orig_context_path = context_path)
|
239
|
+
context_path = __core_file?(context_path) ? File.dirname(context_path) : context_path
|
240
|
+
orig_context_path = __core_file?(orig_context_path) ? File.dirname(orig_context_path) : orig_context_path
|
241
|
+
|
242
|
+
full_pattern = File.expand_path(pattern, context_path)
|
243
|
+
file_paths = __core_find_files_from_pattern(full_pattern).map(&:unicode_normalize)
|
244
|
+
|
245
|
+
file_paths.reject! do |path|
|
246
|
+
File.directory?(path)
|
247
|
+
end
|
248
|
+
|
249
|
+
# remove any files that should be ignored
|
250
|
+
file_paths.reject! do |path|
|
251
|
+
ignored_patterns.any? { |exclude_pattern| File.fnmatch(exclude_pattern, path) }
|
252
|
+
end
|
253
|
+
|
254
|
+
file_paths = self.class.group_filter_paths(file_paths, groups)
|
255
|
+
|
256
|
+
# create relative path to context path
|
257
|
+
self.class.relative_paths_from(file_paths, orig_context_path)
|
258
|
+
end
|
259
|
+
|
260
|
+
def __core_find_files_from_pattern(pattern)
|
261
|
+
raise 'Implement this in subclass'
|
262
|
+
end
|
263
|
+
|
264
|
+
def __core_file?(path)
|
265
|
+
raise 'Implement this in subclass'
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|