epuber 0.3.5

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.
Files changed (206) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +46 -0
  5. data/bin/epuber +10 -0
  6. data/epuber.gemspec +51 -0
  7. data/lib/epuber.rb +14 -0
  8. data/lib/epuber/book.rb +267 -0
  9. data/lib/epuber/book/contributor.rb +90 -0
  10. data/lib/epuber/book/file_request.rb +68 -0
  11. data/lib/epuber/book/target.rb +284 -0
  12. data/lib/epuber/book/toc_item.rb +130 -0
  13. data/lib/epuber/checker.rb +19 -0
  14. data/lib/epuber/checker/text_checker.rb +129 -0
  15. data/lib/epuber/checker_transformer_base.rb +71 -0
  16. data/lib/epuber/command.rb +65 -0
  17. data/lib/epuber/command/compile.rb +142 -0
  18. data/lib/epuber/command/init.rb +145 -0
  19. data/lib/epuber/command/server.rb +59 -0
  20. data/lib/epuber/compiler.rb +255 -0
  21. data/lib/epuber/compiler/compilation_context.rb +86 -0
  22. data/lib/epuber/compiler/file_finders/abstract.rb +270 -0
  23. data/lib/epuber/compiler/file_finders/imaginary.rb +167 -0
  24. data/lib/epuber/compiler/file_finders/normal.rb +22 -0
  25. data/lib/epuber/compiler/file_resolver.rb +316 -0
  26. data/lib/epuber/compiler/file_types/abstract_file.rb +119 -0
  27. data/lib/epuber/compiler/file_types/bade_file.rb +39 -0
  28. data/lib/epuber/compiler/file_types/container_xml_file.rb +26 -0
  29. data/lib/epuber/compiler/file_types/generated_file.rb +29 -0
  30. data/lib/epuber/compiler/file_types/ibooks_display_options_file.rb +31 -0
  31. data/lib/epuber/compiler/file_types/image_file.rb +42 -0
  32. data/lib/epuber/compiler/file_types/mime_type_file.rb +20 -0
  33. data/lib/epuber/compiler/file_types/nav_file.rb +42 -0
  34. data/lib/epuber/compiler/file_types/opf_file.rb +27 -0
  35. data/lib/epuber/compiler/file_types/source_file.rb +52 -0
  36. data/lib/epuber/compiler/file_types/static_file.rb +18 -0
  37. data/lib/epuber/compiler/file_types/stylus_file.rb +20 -0
  38. data/lib/epuber/compiler/file_types/xhtml_file.rb +102 -0
  39. data/lib/epuber/compiler/generator.rb +67 -0
  40. data/lib/epuber/compiler/meta_inf_generator.rb +41 -0
  41. data/lib/epuber/compiler/nav_generator.rb +201 -0
  42. data/lib/epuber/compiler/opf_generator.rb +284 -0
  43. data/lib/epuber/compiler/xhtml_processor.rb +254 -0
  44. data/lib/epuber/config.rb +133 -0
  45. data/lib/epuber/dsl/attribute.rb +248 -0
  46. data/lib/epuber/dsl/attribute_support.rb +130 -0
  47. data/lib/epuber/dsl/object.rb +145 -0
  48. data/lib/epuber/dsl/tree_object.rb +101 -0
  49. data/lib/epuber/helper.rb +19 -0
  50. data/lib/epuber/lockfile.rb +55 -0
  51. data/lib/epuber/plugin.rb +108 -0
  52. data/lib/epuber/ruby_extensions/match_data.rb +40 -0
  53. data/lib/epuber/ruby_extensions/thread.rb +13 -0
  54. data/lib/epuber/server.rb +614 -0
  55. data/lib/epuber/server/auto_refresh/auto_refresh.coffee +97 -0
  56. data/lib/epuber/server/auto_refresh/connector.coffee +125 -0
  57. data/lib/epuber/server/auto_refresh/protocol.coffee +41 -0
  58. data/lib/epuber/server/auto_refresh/reloader.coffee +261 -0
  59. data/lib/epuber/server/base.styl +3 -0
  60. data/lib/epuber/server/basic.styl +211 -0
  61. data/lib/epuber/server/book_content.styl +39 -0
  62. data/lib/epuber/server/default_cover.png +0 -0
  63. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
  64. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
  65. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
  66. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
  67. data/lib/epuber/server/handlers.rb +67 -0
  68. data/lib/epuber/server/keyboard_control.coffee +6 -0
  69. data/lib/epuber/server/pages/book.bade +75 -0
  70. data/lib/epuber/server/pages/common.bade +59 -0
  71. data/lib/epuber/server/pages/files.bade +17 -0
  72. data/lib/epuber/server/pages/toc.bade +29 -0
  73. data/lib/epuber/server/support.coffee +10 -0
  74. data/lib/epuber/templates/template.bookspec +143 -0
  75. data/lib/epuber/third_party/bower.rb +22 -0
  76. data/lib/epuber/third_party/bower/bower.json +10 -0
  77. data/lib/epuber/third_party/bower/bower_components/cookies-js/bower.json +10 -0
  78. data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.d.ts +33 -0
  79. data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.js +173 -0
  80. data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.min.js +6 -0
  81. data/lib/epuber/third_party/bower/bower_components/jquery/MIT-LICENSE.txt +21 -0
  82. data/lib/epuber/third_party/bower/bower_components/jquery/bower.json +28 -0
  83. data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.js +9210 -0
  84. data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.js +5 -0
  85. data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.map +1 -0
  86. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax.js +786 -0
  87. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/jsonp.js +89 -0
  88. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/load.js +75 -0
  89. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseJSON.js +13 -0
  90. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseXML.js +28 -0
  91. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/script.js +64 -0
  92. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/nonce.js +5 -0
  93. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/rquery.js +3 -0
  94. data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/xhr.js +136 -0
  95. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes.js +11 -0
  96. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/attr.js +141 -0
  97. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/classes.js +158 -0
  98. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/prop.js +94 -0
  99. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/support.js +35 -0
  100. data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/val.js +161 -0
  101. data/lib/epuber/third_party/bower/bower_components/jquery/src/callbacks.js +205 -0
  102. data/lib/epuber/third_party/bower/bower_components/jquery/src/core.js +502 -0
  103. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/access.js +60 -0
  104. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/init.js +123 -0
  105. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/parseHTML.js +39 -0
  106. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/ready.js +97 -0
  107. data/lib/epuber/third_party/bower/bower_components/jquery/src/core/var/rsingleTag.js +4 -0
  108. data/lib/epuber/third_party/bower/bower_components/jquery/src/css.js +450 -0
  109. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/addGetHookIf.js +22 -0
  110. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/curCSS.js +57 -0
  111. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/defaultDisplay.js +70 -0
  112. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/hiddenVisibleSelectors.js +15 -0
  113. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/support.js +96 -0
  114. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/swap.js +28 -0
  115. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/cssExpand.js +3 -0
  116. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/getStyles.js +12 -0
  117. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/isHidden.js +13 -0
  118. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rmargin.js +3 -0
  119. data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rnumnonpx.js +5 -0
  120. data/lib/epuber/third_party/bower/bower_components/jquery/src/data.js +178 -0
  121. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/Data.js +181 -0
  122. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/accepts.js +20 -0
  123. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_priv.js +5 -0
  124. data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_user.js +5 -0
  125. data/lib/epuber/third_party/bower/bower_components/jquery/src/deferred.js +149 -0
  126. data/lib/epuber/third_party/bower/bower_components/jquery/src/deprecated.js +13 -0
  127. data/lib/epuber/third_party/bower/bower_components/jquery/src/dimensions.js +50 -0
  128. data/lib/epuber/third_party/bower/bower_components/jquery/src/effects.js +648 -0
  129. data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/Tween.js +114 -0
  130. data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/animatedSelector.js +13 -0
  131. data/lib/epuber/third_party/bower/bower_components/jquery/src/event.js +868 -0
  132. data/lib/epuber/third_party/bower/bower_components/jquery/src/event/ajax.js +13 -0
  133. data/lib/epuber/third_party/bower/bower_components/jquery/src/event/alias.js +39 -0
  134. data/lib/epuber/third_party/bower/bower_components/jquery/src/event/support.js +9 -0
  135. data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/amd.js +24 -0
  136. data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/global.js +32 -0
  137. data/lib/epuber/third_party/bower/bower_components/jquery/src/intro.js +44 -0
  138. data/lib/epuber/third_party/bower/bower_components/jquery/src/jquery.js +37 -0
  139. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation.js +580 -0
  140. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/_evalUrl.js +18 -0
  141. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/support.js +32 -0
  142. data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/var/rcheckableType.js +3 -0
  143. data/lib/epuber/third_party/bower/bower_components/jquery/src/offset.js +207 -0
  144. data/lib/epuber/third_party/bower/bower_components/jquery/src/outro.js +1 -0
  145. data/lib/epuber/third_party/bower/bower_components/jquery/src/queue.js +142 -0
  146. data/lib/epuber/third_party/bower/bower_components/jquery/src/queue/delay.js +22 -0
  147. data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-native.js +172 -0
  148. data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-sizzle.js +14 -0
  149. data/lib/epuber/third_party/bower/bower_components/jquery/src/selector.js +1 -0
  150. data/lib/epuber/third_party/bower/bower_components/jquery/src/serialize.js +111 -0
  151. data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.js +2067 -0
  152. data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.js +3 -0
  153. data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.map +1 -0
  154. data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing.js +199 -0
  155. data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/findFilter.js +100 -0
  156. data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/var/rneedsContext.js +6 -0
  157. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/arr.js +3 -0
  158. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/class2type.js +4 -0
  159. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/concat.js +5 -0
  160. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/hasOwn.js +5 -0
  161. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/indexOf.js +5 -0
  162. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/pnum.js +3 -0
  163. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/push.js +5 -0
  164. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/rnotwhite.js +3 -0
  165. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/slice.js +5 -0
  166. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/strundefined.js +3 -0
  167. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/support.js +4 -0
  168. data/lib/epuber/third_party/bower/bower_components/jquery/src/var/toString.js +5 -0
  169. data/lib/epuber/third_party/bower/bower_components/jquery/src/wrap.js +79 -0
  170. data/lib/epuber/third_party/bower/bower_components/keymaster/MIT-LICENSE +20 -0
  171. data/lib/epuber/third_party/bower/bower_components/keymaster/Makefile +4 -0
  172. data/lib/epuber/third_party/bower/bower_components/keymaster/README.markdown +212 -0
  173. data/lib/epuber/third_party/bower/bower_components/keymaster/bower.json +27 -0
  174. data/lib/epuber/third_party/bower/bower_components/keymaster/keymaster.js +296 -0
  175. data/lib/epuber/third_party/bower/bower_components/keymaster/package.json +11 -0
  176. data/lib/epuber/third_party/bower/bower_components/keymaster/test.html +93 -0
  177. data/lib/epuber/third_party/bower/bower_components/spin.js/LICENSE.txt +21 -0
  178. data/lib/epuber/third_party/bower/bower_components/spin.js/README.md +21 -0
  179. data/lib/epuber/third_party/bower/bower_components/spin.js/bower.json +18 -0
  180. data/lib/epuber/third_party/bower/bower_components/spin.js/jquery.spin.js +80 -0
  181. data/lib/epuber/third_party/bower/bower_components/spin.js/spin.js +337 -0
  182. data/lib/epuber/third_party/bower/bower_components/uri.js/LICENSE.txt +21 -0
  183. data/lib/epuber/third_party/bower/bower_components/uri.js/README.md +534 -0
  184. data/lib/epuber/third_party/bower/bower_components/uri.js/bower.json +16 -0
  185. data/lib/epuber/third_party/bower/bower_components/uri.js/contributing.md +19 -0
  186. data/lib/epuber/third_party/bower/bower_components/uri.js/src/IPv6.js +188 -0
  187. data/lib/epuber/third_party/bower/bower_components/uri.js/src/SecondLevelDomains.js +241 -0
  188. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentQuery.js +104 -0
  189. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentURI.js +97 -0
  190. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.js +2115 -0
  191. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.min.js +86 -0
  192. data/lib/epuber/third_party/bower/bower_components/uri.js/src/URITemplate.js +499 -0
  193. data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.js +235 -0
  194. data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.min.js +7 -0
  195. data/lib/epuber/third_party/bower/bower_components/uri.js/src/punycode.js +508 -0
  196. data/lib/epuber/transformer.rb +19 -0
  197. data/lib/epuber/transformer/text_transformer.rb +60 -0
  198. data/lib/epuber/user_interface.rb +186 -0
  199. data/lib/epuber/vendor/globals_context.rb +26 -0
  200. data/lib/epuber/vendor/hash_binding.rb +26 -0
  201. data/lib/epuber/vendor/nokogiri_extensions.rb +30 -0
  202. data/lib/epuber/vendor/ruby_templater.rb +71 -0
  203. data/lib/epuber/vendor/size.rb +20 -0
  204. data/lib/epuber/vendor/version.rb +83 -0
  205. data/lib/epuber/version.rb +4 -0
  206. 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