1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
# frozen_string_literal: true
require 'sprockets/http_utils'
require 'sprockets/processor_utils'
require 'sprockets/utils'
module Sprockets
module Transformers
include HTTPUtils, ProcessorUtils, Utils
# Public: Two level mapping of a source mime type to a target mime type.
#
# environment.transformers
# # => { 'text/coffeescript' => {
# 'application/javascript' => ConvertCoffeeScriptToJavaScript
# }
# }
#
def transformers
config[:transformers]
end
Transformer = Struct.new :from, :to, :proc
# Public: Register a transformer from and to a mime type.
#
# from - String mime type
# to - String mime type
# proc - Callable block that accepts an input Hash.
#
# Examples
#
# register_transformer 'text/coffeescript', 'application/javascript',
# ConvertCoffeeScriptToJavaScript
#
# register_transformer 'image/svg+xml', 'image/png', ConvertSvgToPng
#
# Returns nothing.
def register_transformer(from, to, proc)
self.config = hash_reassoc(config, :registered_transformers) do |transformers|
transformers << Transformer.new(from, to, proc)
end
compute_transformers!(self.config[:registered_transformers])
end
# Internal: Register transformer for existing type adding a suffix.
#
# types - Array of existing mime type Strings
# type_format - String suffix formatting string
# extname - String extension to append
# processor - Callable block that accepts an input Hash.
#
# Returns nothing.
def register_transformer_suffix(types, type_format, extname, processor)
Array(types).each do |type|
extensions, charset = mime_types[type].values_at(:extensions, :charset)
parts = type.split('/')
suffix_type = type_format.sub('\1', parts[0]).sub('\2', parts[1])
extensions = extensions.map { |ext| "#{ext}#{extname}" }
register_mime_type(suffix_type, extensions: extensions, charset: charset)
register_transformer(suffix_type, type, processor)
end
end
# Internal: Resolve target mime type that the source type should be
# transformed to.
#
# type - String from mime type
# accept - String accept type list (default: '*/*')
#
# Examples
#
# resolve_transform_type('text/plain', 'text/plain')
# # => 'text/plain'
#
# resolve_transform_type('image/svg+xml', 'image/png, image/*')
# # => 'image/png'
#
# resolve_transform_type('text/css', 'image/png')
# # => nil
#
# Returns String mime type or nil is no type satisfied the accept value.
def resolve_transform_type(type, accept)
find_best_mime_type_match(accept || '*/*', [type].compact + config[:transformers][type].keys)
end
# Internal: Expand accept type list to include possible transformed types.
#
# parsed_accepts - Array of accept q values
#
# Examples
#
# expand_transform_accepts([['application/javascript', 1.0]])
# # => [['application/javascript', 1.0], ['text/coffeescript', 0.8]]
#
# Returns an expanded Array of q values.
def expand_transform_accepts(parsed_accepts)
accepts = []
parsed_accepts.each do |(type, q)|
accepts.push([type, q])
config[:inverted_transformers][type].each do |subtype|
accepts.push([subtype, q * 0.8])
end
end
accepts
end
# Internal: Compose multiple transformer steps into a single processor
# function.
#
# transformers - Two level Hash of a source mime type to a target mime type
# types - Array of mime type steps
#
# Returns Processor.
def compose_transformers(transformers, types, preprocessors, postprocessors)
if types.length < 2
raise ArgumentError, "too few transform types: #{types.inspect}"
end
processors = types.each_cons(2).map { |src, dst|
unless processor = transformers[src][dst]
raise ArgumentError, "missing transformer for type: #{src} to #{dst}"
end
processor
}
compose_transformer_list processors, preprocessors, postprocessors
end
private
def compose_transformer_list(transformers, preprocessors, postprocessors)
processors = []
transformers.each do |processor|
processors.concat postprocessors[processor.from]
processors << processor.proc
processors.concat preprocessors[processor.to]
end
if processors.size > 1
compose_processors(*processors.reverse)
elsif processors.size == 1
processors.first
end
end
def compute_transformers!(registered_transformers)
preprocessors = self.config[:preprocessors]
postprocessors = self.config[:postprocessors]
transformers = Hash.new { {} }
inverted_transformers = Hash.new { Set.new }
incoming_edges = registered_transformers.group_by(&:from)
registered_transformers.each do |t|
traversals = dfs_paths([t]) { |k| incoming_edges.fetch(k.to, []) }
traversals.each do |nodes|
src, dst = nodes.first.from, nodes.last.to
processor = compose_transformer_list nodes, preprocessors, postprocessors
transformers[src] = {} unless transformers.key?(src)
transformers[src][dst] = processor
inverted_transformers[dst] = Set.new unless inverted_transformers.key?(dst)
inverted_transformers[dst] << src
end
end
self.config = hash_reassoc(config, :transformers) { transformers }
self.config = hash_reassoc(config, :inverted_transformers) { inverted_transformers }
end
end
end
|