epuber 0.8.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +9 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +2 -2
  5. data/epuber.gemspec +3 -6
  6. data/lib/epuber/book/contributor.rb +10 -6
  7. data/lib/epuber/book/file_request.rb +2 -2
  8. data/lib/epuber/book/target.rb +10 -10
  9. data/lib/epuber/book.rb +2 -2
  10. data/lib/epuber/checker/text_checker.rb +14 -6
  11. data/lib/epuber/checker_transformer_base.rb +1 -1
  12. data/lib/epuber/command/build.rb +6 -1
  13. data/lib/epuber/command/from_file.rb +39 -0
  14. data/lib/epuber/command/init.rb +11 -9
  15. data/lib/epuber/command/server.rb +1 -1
  16. data/lib/epuber/command.rb +1 -0
  17. data/lib/epuber/compiler/file_database.rb +2 -2
  18. data/lib/epuber/compiler/file_finders/abstract.rb +3 -3
  19. data/lib/epuber/compiler/file_resolver.rb +3 -2
  20. data/lib/epuber/compiler/file_types/abstract_file.rb +1 -3
  21. data/lib/epuber/compiler/file_types/bade_file.rb +9 -9
  22. data/lib/epuber/compiler/file_types/css_file.rb +84 -0
  23. data/lib/epuber/compiler/file_types/source_file.rb +31 -0
  24. data/lib/epuber/compiler/file_types/stylus_file.rb +4 -3
  25. data/lib/epuber/compiler/nav_generator.rb +5 -5
  26. data/lib/epuber/compiler/opf_generator.rb +4 -4
  27. data/lib/epuber/compiler/xhtml_processor.rb +7 -25
  28. data/lib/epuber/compiler.rb +12 -7
  29. data/lib/epuber/config.rb +3 -3
  30. data/lib/epuber/dsl/attribute.rb +1 -1
  31. data/lib/epuber/dsl/attribute_support.rb +4 -4
  32. data/lib/epuber/dsl/object.rb +2 -2
  33. data/lib/epuber/from_file/bookspec_generator.rb +371 -0
  34. data/lib/epuber/from_file/encryption_handler.rb +146 -0
  35. data/lib/epuber/from_file/from_file_executor.rb +140 -0
  36. data/lib/epuber/from_file/nav_file.rb +163 -0
  37. data/lib/epuber/from_file/opf_file.rb +219 -0
  38. data/lib/epuber/plugin.rb +1 -1
  39. data/lib/epuber/server.rb +17 -17
  40. data/lib/epuber/transformer/book_transformer.rb +108 -0
  41. data/lib/epuber/transformer.rb +2 -0
  42. data/lib/epuber/user_interface.rb +2 -2
  43. data/lib/epuber/vendor/ruby_templater.rb +3 -3
  44. data/lib/epuber/vendor/version.rb +3 -3
  45. data/lib/epuber/version.rb +1 -1
  46. metadata +40 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f0fcab4dfe82bc1ae829738a62f4915f02e79bbc105ab9eafd6d2c0a664dc60
4
- data.tar.gz: 4fcaf730a6caf5d560a1525b5a2a000d29efec2819c82e7c1fdd1bc4cd470228
3
+ metadata.gz: e7acd7fcb74da144389521daa83a8fb681015b82d5cc8a1da35de6058d26fec3
4
+ data.tar.gz: d61a1ac243fc871f6bf2a114d24936b123ed5a28666057b76fb3fc0da6109265
5
5
  SHA512:
6
- metadata.gz: 4fd75c5cda4801241fa762d728308c3c1ebe9d668cc8cf4f9c7be3b3df48ca4715ec2498a2a4914a0c138c21adf49ecdcc66f49e47a33d0487107dd4b05a4d1b
7
- data.tar.gz: 14e7bb639a81535df13bde1f5f92ecb29cf2892d5692d93ee1b97b1045cd650c4480cccfc7262280e2ad135a31796cb04932993d6e0dac21f0cbfaa226544045
6
+ metadata.gz: 85a24b75e370556e3a028e0c118b4f4bc36395c4140472ddfde4be4c46fd95549fecf3d18c28d72bf545b13f9850d82394ea509b8a97d9e3f1078f678d28647e
7
+ data.tar.gz: 62483b8f634b572336cbede86754baa95d3627ac0c69d4f47e44ca701e57b201b53e0b9568a22fd7d4c3ebee6f345c1b132012be303f0ab73ed4db26d332deb3
data/Gemfile CHANGED
@@ -5,6 +5,15 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in Epuber.gemspec
6
6
  gemspec
7
7
 
8
+ # dev
9
+ gem 'fakefs', '>= 1.3', '< 3.0' # 2.0.0 is not compatible with Ruby 2.5
10
+ gem 'rake', '~> 13.0'
11
+ gem 'rspec', '~> 3.2'
12
+ gem 'rubocop', '~> 1.14'
13
+
14
+ # VSCode plugins
15
+ gem 'ruby-lsp', require: false
16
+
8
17
  group :dev, optional: true do
9
18
  gem 'rubocop-rspec', require: false
10
19
  end
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Roman Kříž
3
+ Copyright (c) 2015-2023 Roman Kříž
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -104,9 +104,9 @@ To install this gem onto your local machine, run `bundle exec rake install`.
104
104
 
105
105
  ## Contributing
106
106
 
107
- Bug reports and pull requests are welcome on GitHub at https://github.com/epuber-io/epuber. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
107
+ Bug reports and pull requests are welcome on GitHub at https://github.com/epuber-io/epuber. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct.
108
108
 
109
109
 
110
110
  ## License
111
111
 
112
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
112
+ The gem is available as open source under the terms of the [MIT License](./LICENSE.txt).
data/epuber.gemspec CHANGED
@@ -11,10 +11,10 @@ Gem::Specification.new do |spec|
11
11
  spec.homepage = Epuber::HOME_URL
12
12
  spec.license = 'MIT'
13
13
  spec.required_ruby_version = '>= 2.5'
14
+ spec.metadata['rubygems_mfa_required'] = 'true'
14
15
 
15
16
  spec.files = Dir['bin/**/*'] + Dir['lib/**/*'] + %w[epuber.gemspec Gemfile LICENSE.txt README.md]
16
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ['lib']
19
19
 
20
20
  spec.add_runtime_dependency 'activesupport', '>= 6.0', '< 8.0'
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'mime-types', '~> 3.0'
25
25
  spec.add_runtime_dependency 'nokogiri', '~> 1.8', '>= 1.8.2'
26
26
  spec.add_runtime_dependency 'os', '~> 1.0'
27
+ spec.add_runtime_dependency 'uuidtools', '~> 2.1'
27
28
 
28
29
  spec.add_runtime_dependency 'sinatra', '>= 2.0', '< 4.0'
29
30
  spec.add_runtime_dependency 'sinatra-contrib', '>= 2.0', '< 4.0'
@@ -37,10 +38,6 @@ Gem::Specification.new do |spec|
37
38
 
38
39
  spec.add_runtime_dependency 'bade', '~> 0.3', '>= 0.3.1'
39
40
  spec.add_runtime_dependency 'coffee-script', '~> 2.4'
41
+ spec.add_runtime_dependency 'css_parser', '>= 0.8', '< 2.0'
40
42
  spec.add_runtime_dependency 'epuber-stylus', '~> 1.1', '>= 1.1.1'
41
-
42
- spec.add_development_dependency 'fakefs', '>= 1.3', '< 3.0' # 2.0.0 is not compatible with Ruby 2.5
43
- spec.add_development_dependency 'rake', '~> 13.0'
44
- spec.add_development_dependency 'rspec', '~> 3.2'
45
- spec.add_development_dependency 'rubocop', '~> 1.14'
46
43
  end
@@ -31,22 +31,26 @@ module Epuber
31
31
 
32
32
  # Creates new instance of Contributor dependent on obj content
33
33
  #
34
- # @param obj [Hash<Symbol, String>, Array<Hash<Symbol,String>, String, Array<String>] input object
35
- # @param role [String] role of contributor
34
+ # @param [Hash<Symbol, String>, Array<Hash<Symbol,String>, String, Array<String>] obj input object
35
+ # @param [String] role role of contributor
36
36
  #
37
37
  # @return [Contributor]
38
38
  #
39
- def self.from_obj(obj, role)
39
+ def self.from_obj(obj, role = 'aut')
40
40
  if obj.is_a?(String)
41
- components = obj.split(' ')
41
+ components = obj.split
42
42
  if components.length >= 2
43
43
  NormalContributor.new(components.first(components.length - 1).join(' '), components.last, role)
44
+ else
45
+ Contributor.new(obj, obj, role)
44
46
  end
45
47
  elsif obj.is_a?(Hash)
46
48
  if obj.key?(:first_name)
47
- NormalContributor.new(obj[:first_name], obj[:last_name], role)
49
+ NormalContributor.new(obj[:first_name], obj[:last_name], obj[:role] || role)
48
50
  elsif obj.key?(:file_as)
49
- Contributor.new(obj[:pretty_name], obj[:file_as], role)
51
+ Contributor.new(obj[:pretty_name], obj[:file_as], obj[:role] || role)
52
+ elsif obj.key?(:name)
53
+ Contributor.from_obj(obj[:name], obj[:role] || role)
50
54
  end
51
55
  end
52
56
  end
@@ -45,10 +45,10 @@ module Epuber
45
45
  # @return [Numeric]
46
46
  #
47
47
  def hash
48
- @source_pattern.hash ^ @group.hash ^ @only_one.hash
48
+ [@source_pattern, @group, @only_one].hash
49
49
  end
50
50
 
51
- # @param other [String, self]
51
+ # @param [String, self] other
52
52
  #
53
53
  def ==(other)
54
54
  if other.is_a?(String)
@@ -215,8 +215,8 @@ module Epuber
215
215
  inherited: false
216
216
 
217
217
 
218
- # @param file_path [String | Epuber::Book::File]
219
- # @param group [Symbol]
218
+ # @param [String | Epuber::Book::File] file_path
219
+ # @param [Symbol] group
220
220
  #
221
221
  # @return [Epuber::Book::File] created file
222
222
  #
@@ -238,7 +238,7 @@ module Epuber
238
238
  end
239
239
  end
240
240
 
241
- # @param file_paths [Array<String>]
241
+ # @param [Array<String>] file_paths
242
242
  #
243
243
  # @return [void]
244
244
  #
@@ -249,8 +249,8 @@ module Epuber
249
249
  end
250
250
  end
251
251
 
252
- # @param key [String]
253
- # @param value [String]
252
+ # @param [String] key
253
+ # @param [String] value
254
254
  #
255
255
  # @return [void]
256
256
  #
@@ -262,7 +262,7 @@ module Epuber
262
262
  end
263
263
  end
264
264
 
265
- # @param file_paths [Array<String>]
265
+ # @param [Array<String>] file_paths
266
266
  #
267
267
  # @return [void]
268
268
  #
@@ -278,7 +278,7 @@ module Epuber
278
278
  # Add default styles to default target, default styles will be automatically added to xhtml document
279
279
  #
280
280
  # Only difference with #add_default_style is it adds multiple files with one pattern
281
- # @param file_paths [Array<String>]
281
+ # @param [Array<String>] file_paths
282
282
  #
283
283
  # @return [void]
284
284
  #
@@ -291,7 +291,7 @@ module Epuber
291
291
  end
292
292
  end
293
293
 
294
- # @param file_paths [Array<String>]
294
+ # @param [Array<String>] file_paths
295
295
  #
296
296
  # @return [void]
297
297
  #
@@ -307,7 +307,7 @@ module Epuber
307
307
  # Add default scripts to target, default scripts will be automatically added to xhtml document
308
308
  #
309
309
  # Only difference with #add_default_script is it adds multiple files with one pattern
310
- # @param file_paths [Array<String>]
310
+ # @param [Array<String>] file_paths
311
311
  #
312
312
  # @return [void]
313
313
  #
@@ -330,7 +330,7 @@ module Epuber
330
330
  yield(@root_toc, self) if block_given?
331
331
  end
332
332
 
333
- # @param path [String] use some file/module/package
333
+ # @param [String] path use some file/module/package
334
334
  #
335
335
  # @return [nil]
336
336
  #
data/lib/epuber/book.rb CHANGED
@@ -160,7 +160,7 @@ module Epuber
160
160
  container: Array,
161
161
  required: true,
162
162
  singularize: true,
163
- auto_convert: { [String, Hash] => ->(value) { Contributor.from_obj(value, 'aut') } }
163
+ auto_convert: { [String, Hash] => ->(value) { Contributor.from_obj(value) } }
164
164
 
165
165
 
166
166
  # @return [String] publisher name
@@ -294,7 +294,7 @@ module Epuber
294
294
 
295
295
  # Finds target with name or nil when not found
296
296
  #
297
- # @param target_name [Symbol, String, Epuber::Book::Target]
297
+ # @param [Symbol, String, Epuber::Book::Target] target_name
298
298
  #
299
299
  # @return [Epuber::Book::Target, nil]
300
300
  #
@@ -10,9 +10,9 @@ module Epuber
10
10
  class Checker
11
11
  class TextChecker < Checker
12
12
  class MatchProblem < Compiler::Problem
13
- # @param message [String]
14
- # @param file_path [String]
15
- # @param match [MatchData]
13
+ # @param [String] message
14
+ # @param [String] file_path
15
+ # @param [MatchData] match
16
16
  #
17
17
  def initialize(match, message, file_path)
18
18
  whole_text = match.pre_match + match.matched_string + match.post_match
@@ -36,8 +36,8 @@ module Epuber
36
36
 
37
37
 
38
38
 
39
- # @param file_path [String]
40
- # @param text [String]
39
+ # @param [String] file_path
40
+ # @param [String] text
41
41
  # @param [CompilationContext] compilation_context
42
42
  #
43
43
  # @return nil
@@ -52,7 +52,7 @@ module Epuber
52
52
  @file_path = nil
53
53
  end
54
54
 
55
- # @param regexp [Regexp]
55
+ # @param [Regexp] regexp
56
56
  # @param [String] message message to display, when the regexp found something
57
57
  #
58
58
  def should_not_contain(regexp, message)
@@ -65,6 +65,14 @@ module Epuber
65
65
  Config.instance.pretty_path_from_project(file_path))
66
66
  end
67
67
  end
68
+
69
+ def error(message, location: nil)
70
+ super(message, location: location || Config.instance.pretty_path_from_project(file_path))
71
+ end
72
+
73
+ def warning(message, location: nil)
74
+ super(message, location: location || Config.instance.pretty_path_from_project(file_path))
75
+ end
68
76
  end
69
77
  end
70
78
  end
@@ -55,7 +55,7 @@ module Epuber
55
55
  {}
56
56
  end
57
57
 
58
- # @param type [Symbol]
58
+ # @param [Symbol] type
59
59
  #
60
60
  # @return [Class]
61
61
  #
@@ -26,7 +26,12 @@ module Epuber
26
26
  ].concat(super)
27
27
  end
28
28
 
29
- # @param argv [CLAide::ARGV]
29
+ # To resolve problem with `compile` treating as subcommand
30
+ def self.subcommands
31
+ []
32
+ end
33
+
34
+ # @param [CLAide::ARGV] argv
30
35
  #
31
36
  def initialize(argv)
32
37
  @targets_names = argv.arguments!
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+ require_relative '../from_file/from_file_executor'
5
+
6
+ module Epuber
7
+ class Command
8
+ class FromFile < Command
9
+ self.summary = 'Initialize current folder to use it as Epuber project from existing EPUB file'
10
+ self.arguments = [
11
+ CLAide::Argument.new('EPUB_FILE', true),
12
+ ]
13
+
14
+ # @param [CLAide::ARGV] argv
15
+ #
16
+ def initialize(argv)
17
+ @filepath = argv.arguments!.first
18
+
19
+ super(argv)
20
+ end
21
+
22
+ def validate!
23
+ super
24
+
25
+ help! 'You must specify path to existing EPUB file' if @filepath.nil?
26
+ help! "File #{@filepath} doesn't exists" unless File.exist?(@filepath)
27
+
28
+ existing = Dir.glob('*.bookspec')
29
+ help! "Can't reinit this folder, #{existing.first} already exists." unless existing.empty?
30
+ end
31
+
32
+ def run
33
+ super
34
+
35
+ FromFileExecutor.new(@filepath).run
36
+ end
37
+ end
38
+ end
39
+ end
@@ -11,10 +11,10 @@ module Epuber
11
11
  class Init < Command
12
12
  self.summary = 'Initialize current folder to use it as Epuber project'
13
13
  self.arguments = [
14
- CLAide::Argument.new('PROJECT_NAME', false, true),
14
+ CLAide::Argument.new('PROJECT_NAME', false),
15
15
  ]
16
16
 
17
- # @param argv [CLAide::ARGV]
17
+ # @param [CLAide::ARGV] argv
18
18
  #
19
19
  def initialize(argv)
20
20
  @book_name = argv.arguments!.first
@@ -23,6 +23,8 @@ module Epuber
23
23
  end
24
24
 
25
25
  def validate!
26
+ super
27
+
26
28
  help! 'You must specify identifier-like name of the book as first argument' if @book_name.nil?
27
29
 
28
30
  existing = Dir.glob('*.bookspec')
@@ -57,7 +59,7 @@ module Epuber
57
59
 
58
60
  # Creates <book-id>.bookspec file from template
59
61
  #
60
- # @param book_id [String]
62
+ # @param [String] book_id
61
63
  #
62
64
  # @return [void]
63
65
  #
@@ -71,7 +73,7 @@ module Epuber
71
73
 
72
74
  # Creates <book-id>.sublime-project
73
75
  #
74
- # @param book_id [String]
76
+ # @param [String] book_id
75
77
  #
76
78
  # @return [void]
77
79
  #
@@ -115,8 +117,8 @@ module Epuber
115
117
  STYLUS
116
118
  end
117
119
 
118
- # @param string [String] text to file
119
- # @param file_path [String] path to file
120
+ # @param [String] string text to file
121
+ # @param [String] file_path path to file
120
122
  #
121
123
  # @return [void]
122
124
  #
@@ -125,8 +127,8 @@ module Epuber
125
127
  UI.puts " #{'create'.ansi.green} #{file_path}"
126
128
  end
127
129
 
128
- # @param string [String] text to file
129
- # @param file_path [String] path to file
130
+ # @param [String] string text to file
131
+ # @param [String] file_path path to file
130
132
  #
131
133
  # @return [void]
132
134
  #
@@ -158,7 +160,7 @@ module Epuber
158
160
  UI.puts " #{'create'.ansi.green} #{dir_path}/"
159
161
  end
160
162
 
161
- # @param text [String]
163
+ # @param [String] text
162
164
  #
163
165
  # @return [String] returned text without new line
164
166
  #
@@ -17,7 +17,7 @@ module Epuber
17
17
  ].concat(super)
18
18
  end
19
19
 
20
- # @param args [CLAide::ARGV]
20
+ # @param [CLAide::ARGV] args
21
21
  #
22
22
  def initialize(args)
23
23
  super
@@ -16,6 +16,7 @@ module Epuber
16
16
  require_relative 'command/compile'
17
17
  require_relative 'command/init'
18
18
  require_relative 'command/server'
19
+ require_relative 'command/from_file'
19
20
 
20
21
  self.abstract_command = true
21
22
  self.command = 'epuber'
@@ -65,7 +65,7 @@ module Epuber
65
65
  end
66
66
 
67
67
  def update_all_metadata
68
- @all_files.each do |file_path, _|
68
+ @all_files.each_key do |file_path|
69
69
  update_metadata(file_path)
70
70
  end
71
71
  end
@@ -98,7 +98,7 @@ module Epuber
98
98
  to_remove = @all_files.keys - file_paths
99
99
  to_remove.each { |key| @all_files.delete(key) }
100
100
 
101
- @all_files.each do |_, stat|
101
+ @all_files.each_value do |stat|
102
102
  _cleanup_stat_dependency_list(file_paths, stat)
103
103
  end
104
104
  end
@@ -194,7 +194,7 @@ module Epuber
194
194
  end.flatten
195
195
 
196
196
  paths.select do |file_path|
197
- valid_extensions.include?(::File.extname(file_path))
197
+ valid_extensions.include?(::File.extname(file_path).downcase)
198
198
  end
199
199
  end
200
200
  end
@@ -247,8 +247,8 @@ module Epuber
247
247
  # @return [Array<String>] list of founded files
248
248
  #
249
249
  def __core_find_files(pattern, groups, context_path, orig_context_path = context_path)
250
- context_path = __core_file?(context_path) ? File.dirname(context_path) : context_path
251
- orig_context_path = __core_file?(orig_context_path) ? File.dirname(orig_context_path) : orig_context_path
250
+ context_path = File.dirname(context_path) if __core_file?(context_path)
251
+ orig_context_path = File.dirname(orig_context_path) if __core_file?(orig_context_path)
252
252
 
253
253
  full_pattern = File.expand_path(pattern, context_path)
254
254
  file_paths = __core_find_files_from_pattern(full_pattern).map(&:unicode_normalize)
@@ -20,7 +20,7 @@ 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
@@ -247,6 +247,7 @@ module Epuber
247
247
  def file_class_for(extname)
248
248
  mapping = {
249
249
  '.styl' => FileTypes::StylusFile,
250
+ '.css' => FileTypes::CSSFile,
250
251
 
251
252
  '.coffee' => FileTypes::CoffeeScriptFile,
252
253
 
@@ -279,7 +280,7 @@ module Epuber
279
280
 
280
281
  private
281
282
 
282
- # @param file [Epuber::Compiler::AbstractFile]
283
+ # @param [Epuber::Compiler::AbstractFile] file
283
284
  #
284
285
  # @return [nil]
285
286
  #
@@ -87,9 +87,7 @@ module Epuber
87
87
  def self.write_to_file!(content, to_path)
88
88
  FileUtils.mkdir_p(File.dirname(to_path))
89
89
 
90
- File.open(to_path, 'w') do |file_handle|
91
- file_handle.write(content)
92
- end
90
+ File.write(to_path, content)
93
91
  end
94
92
  end
95
93
  end
@@ -45,15 +45,7 @@ module Epuber
45
45
  end
46
46
  end
47
47
 
48
- if !precompiled.nil?
49
- xhtml_content = UI.print_step_processing_time('rendering precompiled Bade') do
50
- renderer = Bade::Renderer.from_precompiled(precompiled)
51
- .with_locals(variables)
52
- renderer.file_path = source_path
53
-
54
- renderer.render(new_line: '', indent: '')
55
- end
56
- else
48
+ if precompiled.nil?
57
49
  if compilation_context.incremental_build?
58
50
  UI.print_processing_debug_info('Parsing new version of source file')
59
51
  end
@@ -70,6 +62,14 @@ module Epuber
70
62
  FileUtils.mkdir_p(File.dirname(precompiled_path))
71
63
  renderer.precompiled.write_yaml_to_file(precompiled_path)
72
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
+
73
73
  renderer.render(new_line: '', indent: '')
74
74
  end
75
75
  end
@@ -0,0 +1,84 @@
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
+ parser = UI.print_step_processing_time('css parsing') do
44
+ parser = CssParser::Parser.new
45
+ parser.load_string!(content)
46
+ parser
47
+ end
48
+
49
+ # resolve links to files, add other linked resources and compute correct path
50
+ UI.print_step_processing_time('resolving url()') do
51
+ parser.each_rule_set do |rule_set, _media_types|
52
+ declarations = rule_set.instance_eval { @declarations }
53
+ declarations.each do |property, decl_value|
54
+ value = decl_value.to_s
55
+ next unless value =~ URL_REGEXP
56
+
57
+ path = Regexp.last_match(1)
58
+ if path.start_with?('"') && path.end_with?('"')
59
+ path = path[1..-2]
60
+ quote = '"'
61
+ end
62
+ if path.start_with?("'") && path.end_with?("'")
63
+ path = path[1..-2]
64
+ quote = "'"
65
+ end
66
+
67
+ next if path.start_with?('data:') || path.start_with?('http://') || path.start_with?('https://')
68
+
69
+ resource_group = DECLARATION_TO_FILE_GROUP_MAP[property]
70
+ new_url = SourceFile.resolve_relative_file(destination_path,
71
+ path,
72
+ compilation_context.file_resolver,
73
+ group: resource_group)
74
+ content = content.gsub(value, "url(#{quote}#{new_url}#{quote})")
75
+ end
76
+ end
77
+ end
78
+
79
+ content
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -112,6 +112,37 @@ module Epuber
112
112
  MSG
113
113
  end
114
114
  end
115
+
116
+ def self.resolve_relative_file(destination_path, pattern, file_resolver, group: nil)
117
+ dirname = File.dirname(destination_path)
118
+
119
+ begin
120
+ new_path = file_resolver.dest_finder.find_file(pattern, groups: group, context_path: dirname)
121
+ rescue FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError
122
+ begin
123
+ new_path = XHTMLProcessor.resolved_link_to_file(pattern,
124
+ group,
125
+ dirname,
126
+ file_resolver.source_finder).to_s
127
+ rescue XHTMLProcessor::UnparseableLinkError,
128
+ FileFinders::FileNotFoundError,
129
+ FileFinders::MultipleFilesFoundError => e
130
+ UI.warning(e.to_s, location: img)
131
+ end
132
+ end
133
+
134
+ pkg_abs_path = File.expand_path(new_path, dirname).unicode_normalize
135
+ pkg_new_path = Pathname.new(pkg_abs_path)
136
+ .relative_path_from(Pathname.new(file_resolver.source_path))
137
+ .to_s
138
+
139
+ file_class = FileResolver.file_class_for(File.extname(new_path))
140
+ file = file_class.new(pkg_new_path)
141
+ file.path_type = :manifest
142
+ file_resolver.add_file(file)
143
+
144
+ FileResolver.renamed_file_with_path(new_path)
145
+ end
115
146
  end
116
147
  end
117
148
  end